Android O : Background execution not allowed: Custom broadcast with permission - android

In my application I have registered a broadcast receiver for an implicit broadcast by another application protected with permission :
<receiver
android:name=".receiver.MyReceiver"
android:exported="true"
android:permission="owner.custom.permission">
<intent-filter>
<action android:name="owner.custom.broadcast"/>
</intent-filter>
</receiver>
In MyReceiver#onReceive() I am invoking a JobIntentService MyService using enqueueWork():
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
Log.i(TAG, "Received the broadcast");
MyService.enqueueWork(context, getServiceIntent(context));
}
private Intent getServiceIntent(final Context context) {
final Intent intent = new Intent(context, MyService.class);
intent.putExtra("someKey", true);
return intent;
}
}
I have the following method in MyService :
public static void enqueueWork(final Context context, final Intent work) {
enqueueWork(context, MyService.class, 111, work);
}
Now whenever owner.custom.broadcast is broadcast, MyReceiver is not triggered and I can see the following logs :
07-23 03:56:29.755 3335 3361 W BroadcastQueue: Background execution not allowed: receiving Intent { act=owner.custom.broadcast flg=0x10 } to com.amazon.myApp/.receiver.MyReceiver
Now the thing is I am listening to another such 3rd party implicit broadcast with a different broadcast receiver and invoking MyService over there and it works fine. I am also listening for BOOT_COMPLETED broadcast in a yet another broadcast receiver and invoking MyService over there and it works fine there too.
What are the possible causes for this error which would help me identify if I'm missing something.
UPDATE :
I am now just trying to get the broadcast receiver to trigger but I am still getting the same error. I am trying with nothing but a log line in the receiver :
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
Log.i(TAG, "Received the broadcast");
}
}

Android O limits the implicit broadcast, you can't execute a background service in the receiver.
However, it only limits the static receivers, you can register your receiver in the code to trigger your service.
Of course, in some case, you don't want to do it "programmatically", then you should check the error case, from this link http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java#1275 (Line:1275), I find the error (not sure if this is the same as your system version).
We can see there are few conditions to access to this code block, we analyze them one by one, all we want to do is make the condition equals false:
(r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0
It means if we don't want the background to receive the intent the condition will be true, and usually, we don't add this flag because we want the background to receive the broadcast, go ahead.
r.intent.getComponent() == null
It should not be null in any of our case, go ahead.
r.intent.getPackage() == null
Same above, go ahead.
r.intent.getFlags()&Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0
It means we cannot have a flag called Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, I think we can try this, but you will see this in the Intent class:
/**
* If set, the broadcast will always go to manifest receivers in background (cached
* or not running) apps, regardless of whether that would be done by default. By
* default they will only receive broadcasts if the broadcast has specified an
* explicit component or package name.
*
* NOTE: dumpstate uses this flag numerically, so when its value is changed
* the broadcast code there must also be changed to match.
*
* #hide
*/
public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
It's hidden, but you can just hardcode the integer in your project, now add this flag to your intent to try if your code is work.
intent.addFlags(0x01000000)
Good luck :)
Note: this solution will NOT resolve to receive the system implicit broadcast to run background tasks.

This is an old question, but I found a solution which worked for me.
As mentioned here
Context-registered receivers receive broadcasts as long as their
registering context is valid. For an example, if you register within
an Activity context, you receive broadcasts as long as the activity is
not destroyed. If you register with the Application context, you
receive broadcasts as long as the app is running.
I had to remove the receiver declaration in the Manifest completely and register my receiver during runtime, using the Application context!
IntentFilter filter = new IntentFilter("owner.custom.broadcast");
getContext().getApplicationContext().registerReceiver(new MyReceiver(), filter);
and then
public class MyReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
context.unregisterReceiver(this);
// ....
}
}

Try this
I faced a similar problem year ago, I'm not pretty sure of this, but since its not allowed for background execution then execute it in foreground using Foreground Service, you can achieve that by starting a service that is connected to a notification, then in your service you can trigger your broadcast and that should work.
I hope my answer helps you.

I'm not sure why this solution worked (maybe someone else can elaborate on why) but I was able to get my broadcast receiver to trigger by declaring the permission in my Manifest itself and and also using the same. Find the code changes below :
<permission
android:name="owner.custom.permission"
android:protectionLevel="signatureOrSystem">
</permission>
.
.
.
<uses-permission android:name="owner.custom.permission" />

Related

.Net Maui BroadcastReceiver does not receive anything

I implemented an BroadcastReceiver for Android-Platform to detect whenether the Devices Battery is being Charged or not. Unfortunately, it doesn't seem to work on my Device which has Android 10 installed (Android 10 is my minimum requirement for the App).
I need this BroadcastReceiver to be triggered even if the App is not running. Therefore an Implicit broadcast would be an excellent choice instead of register an BroadcastReceiver while the App is running.
Permissions set within "AndoirdManifest.xml"
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
My PowerConnectedBroadcastReceiver looks like this:
[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { Intent.ActionPowerConnected, Intent.ActionPowerDisconnected, Intent.ActionDockEvent, Intent.ActionBatteryChanged }, Priority = (int)IntentFilterPriority.HighPriority)]
public class PowerConnectedBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Console.WriteLine($"PowerConnectedBroadcastReceiver received an intent: {intent}");
}
}
What am I doing wrong?
Any Kind of advise would be appreciated.
From Broadcast Receivers docs
Apps that target Android 8.0 (API level 26) or higher may not statically register for an implicit broadcast. Apps may still statically register for an explicit broadcast. There is a small list of implicit broadcasts that are exempt from this restriction. These exceptions are described in the Implicit Broadcast Exceptions guide in the Android documentation. Apps that are interested in implicit broadcasts must do so dynamically using the RegisterReceiver method. This is described next.
Dynamic registration
[BroadcastReceiver(Enabled = true, Exported = true)]
public class PowerConnectedBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Console.WriteLine($"PowerConnectedBroadcastReceiver received an intent: {intent}");
}
}
PowerConnectedBroadcastReceiver receiver;
IntentFilter intentFilter;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
receiver = new();
intentFilter = new(Intent.ActionPowerConnected);
intentFilter.AddAction(Intent.ActionPowerDisconnected);
intentFilter.AddAction(Intent.ActionDockEvent);
intentFilter.AddAction(Intent.ActionBatteryChanged);
intentFilter.Priority = (int)IntentFilterPriority.HighPriority;
}
protected override void OnResume()
{
base.OnResume();
RegisterReceiver(receiver, intentFilter);
}
protected override void OnPause()
{
UnregisterReceiver(receiver);
base.OnPause();
}
Edit
As the broadcast receiver is managed inside the activity it has the life cycle of the activity, if you want it to keep running for some reasons even if the app is not running, then i believe you should register/unregister it inside a foreground service.
Related questions:
Android keep BroadcastReceiver in background
How to keep broadcast receiver running in background?
Extra
Failing on un-register the broadcast receiver when the activity is terminated, will result in a leak. UnregisterReceiver() is called in OnPause rather than OnDestry() because the latter is not guaranteed to be called.
Android Activity onDestroy() is not always called and if called only part of the code is executed

Android - How to receive shortcut create result

Looking at the code sample here - I find the following comment puzzling:
// ... We assume here that the
// app has implemented a method called createShortcutResultIntent() that
// returns a broadcast intent.
what does it mean the app has implemented ... where is this implementation done?
is it a broadcast receiver? registered to which intent filter?
is this an abstract method? of which class?
and then I see this code sample - which handles a completely different flow (I think) and I'm lost again
You can obtain feedback via catching the broadcast event which you setup while use requestPinShortcut function.
At first you need a usual broadcast receiver (in the code below it has name ShortcutReceiver). You can even use existing broadcast receiver and simple add new action which it should catch.
Lets the action will be "general.intent.action.SHORTCUT_ADDED" and it will be stored in ShortcutReceiver.kInstalledAction constant. In this case in the manifest you should have:
<receiver android:name=".ShortcutReceiver" >
<intent-filter>
<action android:name="general.intent.action.SHORTCUT_ADDED"/>
</intent-filter>
</receiver>
After this you can use following code in the activity for create a pinned shortcut (in other places change this on object of Context class):
ShortcutManager manager = this.getSystemService(ShortcutManager.class);
Intent targetIntent = new Intent(ShortcutReceiver.kInstalledAction);
targetIntent.setPackage(this.getPackageName());
PendingIntent intent = PendingIntent.getBroadcast(this, 0, targetIntent, 0);
manager.requestPinShortcut(info, intent.getIntentSender());
In this code info is correct object of ShortcutInfo class.
You can handle the event while catch the broadcast:
public class ShortcutReceiver extends BroadcastReceiver {
public static final String kInstalledAction = "general.intent.action.SHORTCUT_ADDED";
#Override
public void onReceive(Context context, Intent intent) {
if (kInstalledAction.equals(intent.getAction())) {
// Handle the event after the shortcut has been added
Toast.makeText(context, "The shortcut has been added", Toast.LENGTH_LONG).show();
}
}
}
Please take into account that from my experience the broadcast event happens after the shortcut has been added but sometimes there can be some delays (at about some minutes). But may be there is some dependency on the launcher.
Update
As described in other answers on Android 8 catching of implicit intent via broadcast in general doesn't work.
So I simple changed the intent to explicit via set package name of the current app. So only our broadcast receiver can catch the intent.
First things first. Implicit intents on Android 8.0 Oreo:
Because Android 8.0 (API level 26) introduces new limitations for broadcast receivers, you should remove any broadcast receivers that are registered for implicit broadcast intents. Leaving them in place does not break your app at build-time or runtime, but they have no effect when your app runs on Android 8.0.
Explicit broadcast intents—those that only your app can respond to—continue to work the same on Android 8.0.
There are exceptions to this new restriction. For a list of implicit broadcasts that still work in apps targeting Android 8.0, see Implicit Broadcast Exceptions.
https://developer.android.com/about/versions/oreo/android-8.0-changes
Note: there are some exceptions: https://developer.android.com/guide/components/broadcast-exceptions (very few)
Instead, we will use the so-called context-registered receiver, it will last as long as our app lives, or until we unregister it.
Also, ShortcutManager requires API 25 that's why we will use it's compat version in order not to duplicate the code for old and new versions. (ShortcutManagerCompat was added in version 26.1.0)
Code to create a pinned shortcut on the Home screen:
public static void addShortcut(Context context, String id) {
if(context == null || note == null)
return;
//there may be various Home screen apps, better check it
if (ShortcutManagerCompat.isRequestPinShortcutSupported(context)){
Intent shortcutIntent = new Intent(context, MainActivity.class);
shortcutIntent.setAction(Constants.ACTION_SHORTCUT); // !!! intent's action must be set on oreo
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, note.get_id().toString())
.setIntent(shortcutIntent)
.setShortLabel("MyShortcut") //recommend max 10 chars
.setLongLabel("Long shortcut name")//recommend max 25 chars
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut))
.build();
//callback if user allowed to place the shortcut
Intent pinnedShortcutCallbackIntent = new Intent(ACTION_SHORTCUT_ADDED_CALLBACK);
PendingIntent successCallback = PendingIntent.getBroadcast(context, REQ_CODE_SHORTCUT_ADDED_CALLBACK,
pinnedShortcutCallbackIntent, 0);
ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, successCallback.getIntentSender());
}
And here is the code to receive the broadcast in your Activity, for example. Note that this "callback" will be called only if your app is running, receiver is registered and the user allowed the shortcut:
private ShortcutAddedReceiver shortcutAddedReceiver;
private void registerShortcutAddedReceiver(){
if(shortcutAddedReceiver == null){
shortcutAddedReceiver = new ShortcutAddedReceiver();
}
IntentFilter shortcutAddedFilter = new IntentFilter(ShortcutHelper.ACTION_SHORTCUT_ADDED_CALLBACK);
registerReceiver(shortcutAddedReceiver, shortcutAddedFilter);
}
private void unregisterShortcutAddedReceiver(){
if(shortcutAddedReceiver != null){
unregisterReceiver(shortcutAddedReceiver);
shortcutAddedReceiver = null;
}
}
#Override
public void onStart() {
super.onStart();
registerShortcutAddedReceiver();
}
#Override
public void onStop() {
super.onStop();
unregisterShortcutAddedReceiver();
}
private class ShortcutAddedReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Snackbar.make(view, "Shortcut added", Snackbar.LENGTH_LONG).show();
}
}
Hope this helps!

Getting SCREEN_ON and SCREEN_OFF intents from a widget

I have a widget and I would like to check if the screen is off or on.
I can't use PowerMananger.isScreenOn because I want to support Android 1.5/1.6 .
So I tried to register SCREEN_ON/SCREEN_OFF actions in the manifest but that doesn't work. Seems like only registerReceiver works for those intents. (Android - how to receive broadcast intents ACTION_SCREEN_ON/OFF?)
The question is, where should I register my widget?
I can't register the screen intents receiver from my widget because you can't call registerReceiver from another BroadcastReceiver that is stated in the manifest.
I thought about calling it in the onCreate of my configuration activity.
The problem is that I don't call unregisterReceiver, so I get an exception for a leak.
Is there any other solution to this?
Thanks.
My solution is to start a service in the public void onReceive(Context context, Intent intent) method in the AppwidgetProvider subclass. Like:
if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_ENABLED)) {
Intent listenerService=new Intent(context,ScreenMoniterService.class);
startService(listenerService);
return;
}
Then in the public void onCreate() method of this service, register the BroadcastReceiver and in the public void onDestroy() method, unregister it.
Of course, you should stop that service when all of the appwidget are deleted.
if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_DISABLED)) {
Intent listenerService=new Intent(context,ScreenMoniterService.class);
stopService(listenerService);
return;
}
registerReceiver:
final IntentFilter bcFilter = new IntentFilter();
bcFilter.addAction(Intent.ACTION_SCREEN_ON);
bcFilter.addAction(Intent.ACTION_SCREEN_OFF);
context.getApplicationContext().registerReceiver(this, bcFilter);
unregisterReceiver:
context.getApplicationContext().unregisterReceiver(this);
(Just at AppWidgetProvider!)

Android activity not getting broadcast from local service

From the examples this looked straightforward. Maybe you can show me what I did wrong. I can't get an activity to receive a broadcast sent from a local service.
I have Activity1 that start Service1:
startService(new Intent(Activity1.this, Service1.class));
Activity1 then starts Activity2:
startActivity(new Intent(Activity1.this, Activity2.class));
Service1, a local service, listens for downloads:
protected final BroadcastReceiver service2DownloadBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(final Context context, final Intent intent)
{
...
broadcastDownloadFinished(Uri.fromFile(downloadedFile));
The broadcast receiver of Service1 then broadcasts its own message:
protected Intent broadcastDownloadFinished(final Uri uri)
{
final Intent intent = new Intent(ACTION_DOWNLOAD_FINISHED).setData(checkNotNull(uri));
sendBroadcast(intent);
Activity2, which is in the foreground at the time, listens for the ACTION_DOWNLOAD_FINISHED intent using its own broadcast receiver:
private final BroadcastReceiver activity2DownloadBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(final Context context, final Intent intent)
{
Log.i(Activity2.class.getSimpleName(), "Received download event: " + intent.getAction() + " " + intent.getData());
Activity2 of course registers the receiver:
protected void onResume()
{
super.onResume();
final IntentFilter downloadIntentFilter = new IntentFilter();
downloadIntentFilter.addAction(ACTION_DOWNLOAD_FINISHED);
registerReceiver(activity2DownloadBroadcastReceiver, downloadIntentFilter);
In case it matters, ACTION_DOWNLOAD_FINISHED is something like "com.example.intent.action.DOWNLOAD_FINISHED".
Service1 receives the download manager event in its receiver and apparently broadcasts its own custom event, but Activity2 never seems to receive it. What did I do wrong? Is it a problem to broadcast an intent in the middle of processing another one? (I wouldn't think so---this is asynchronous, right?)
Update: Just to make sure there is no problem sending a broadcast in the middle of receiving a broadcast, I changed my broadcast code to actually perform the broadcast three seconds later on the main thread:
Log.i(getClass().getSimpleName(), "...ready to broadcast");
final Intent intent = new Intent(ACTION_DOWNLOAD_FINISHED).setData(checkNotNull(uri));
mainThreadHandler.postDelayed(new Runnable()
{
public void run()
{
Log.i(getClass().getSimpleName(), "...broadcasting");
sendBroadcast(intent);
Log.i(getClass().getSimpleName(), "...broadcasted");
}
}, 3000);
Log.i(getClass().getSimpleName(), "...scheduled to broadcast");
As expected, the log says:
...ready to broadcast
...scheduled to broadcast
...broadcasting
...broadcasted
Yet nothing is received in the activity. Please help.
Eureka! I found it! The problem is that I supplied a data URI in my broadcast intent. The Android intent matching rules get a little complicated. If you supply a data URI, then your intent filter must specify a matching MIME type.
Unfortunately, although the Android documentation says that the data type can be inferred from the data URI, apparently Android doesn't know that a file://.../example.jpg is an image. So this doesn't work:
intentFilter.addDataType("image/*");
However, instead of specifying a type, I can specify a scheme that I accept:
intentFilter.addDataScheme("file");
That works! It's a little rough---and a little artificial to restrict my broadcasts to file: URIs, but as that's all I'm using for the moment, it works.
Note that apparently I could manually specify the MIME type in the intent when I broadcast it, but that's too much trouble for now, as I'm downloading images from Picasa so I already know that they are images (and don't care the specific MIME type). And if it gets too much trouble, I could ditch the whole setData() thing altogether and set an extra---but of course I want to do things the Right Way.
have you included your receiver in your activity's manifest?
<receiver
android:name=".YourReceiver">
<intent-filter>
<action
android:name="intent_name"></action>
</intent-filter>
</receiver>

Update the UI of the calling activity or start a new activity when the alarm is triggered from a broadcast receiver

I am writing an alarm code and using a broadcast receiver. I am able to receive the broadcast receiver. but now I want to come back to the calling activity and update the UI of my activity. I am not able to this.
I used the following code in my activity but it is never executing that code.
private BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "I am back!!", Toast.LENGTH_LONG).show();
}
};
#Override
protected void onPause()
{
super.onPause();
unregisterReceiver(myBroadcastReceiver);
}
#Override
protected void onResume()
{
super.onResume();
IntentFilter intentFilter = new IntentFilter("com.test.Main");
registerReceiver(myBroadcastReceiver, intentFilter);
}
in the manifest file I have included the following, here gotAlarm is the broadcast receiver file
<receiver android:name=".gotAlarm"
android:enabled="true">
</receiver>
gotAlarm file is one which gets called from the pending intent of the alarm set
public class gotAlarm extends BroadcastReceiver {
public void onReceive(Context context, Intent intent){
Toast.makeText(context, "Wake Up!!", Toast.LENGTH_LONG).show();
}
}
May be I am missing something very basic.
please help.
Two things:
If you dynamically register the receiver via Context.registerReceiver() then you won't receive broadcasts when Activity is paused (or stopped or not-running). If you need to receive broadcasts even when Activity is paused then create a top-level BroadcastReceiver class (as opposed to your inner class) and use <receiver> to register it.
BroadcastReceiver lifecycle docs state that BroadcastReceiver object is alive only during processing of onReceive(). You can not do any async tasks like showing dialogs, etc.. In your case (Activities might not be running and you receive a broadcast) you should use NotificationManager to notify user something happened.
I have dropped this way and I am starting a new activity on receiving broadcast. And I am sending information data from calling activity to broadcast and from broadcast to next activity. This has served the purpose.
Did you register your BroadcastReceiver (you can do this in the 'onResume'-method of your Activity)? Also, you should unregister your BroadcastReceiver in the 'onPause'-method.

Categories

Resources