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
Related
I have the following service declared in the manifest:
<application
...
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.example.android.test.TestService"
android:process=":Remote"
android:permission="android.permission.WAKE_LOCK"/>
<receiver android:name="com.example.android.test.TestService">
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
</intent-filter>
</receiver>
</application>
and this is the Service class
public class TestService extends Service implements SensorEventListener {
public class BatteryReceiver_andFileChecker extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//TODO
}
// constructor
public BatteryReceiver_andFileChecker(){
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
//TODO
return START_STICKY;
}
#SuppressLint("WakelockTimeout")
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "Logging service started new", Toast.LENGTH_SHORT).show();
//Acquire wake lock
PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WLTAG:MyWakelockTag");
wakeLock.acquire();
//Display notification
this.notIntent = new Intent(this, MainActivity.class);
this.pendingIntent = PendingIntent.getActivity(this, 0, this.notIntent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, this.channelID).setSmallIcon(R.drawable.ic_launcher_background).setContentTitle("Test").setContentText("Sensor is recording").setPriority(NotificationCompat.PRIORITY_DEFAULT).setContentIntent(this.pendingIntent);
startForeground(this.NOTIFICATION, builder.build());
//BatteryCheck
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mReceiver = new BatteryReceiver_andFileChecker();
registerReceiver(mReceiver, filter);
}
#Override
public void onDestroy() {
super.onDestroy();
//cancel notification
stopForeground(true);
//Unregister battery receiver
unregisterReceiver(mReceiver);
//release wakeLock
wakeLock.release();
//Stop Service
stopSelf();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
// super.onBind(intent);
return null;
}
#Override
public void onSensorChanged(SensorEvent event) {
//TODO
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
And this services is created and terminated on two different "onclick" functions from the MainActivity
public class MainActivity extends AppCompatActivity {
...
public void onClickStart(View v) {
// Start Service
this.intent = new Intent(getApplicationContext(), TestService.class);
this.intent.putExtra("foo",foo);
startService(this.intent);
}
public void onClickStopAcquisition(View v) {
// Stop Service
stopService(this.intent);
}
}
This code runs as expecten in Android 6.0.1 and does not work on Android 10...
When I debug de app, no errors are fired when pressed the Start and Stop buttons in both OS, nevertheless, android 6.0.1 fires the service and android 10 does not...
Any reason why?
Just to keep in mind when understanding my implementation, my intention is to make a service which keeps running and doing stuff even if the user is not active in the application. Since I implementen the SensorEventListener to record data from sensors, my intention is to record data while the user might be interacting with the phone or even doing nothing (after he presses the power button of the phone, the service keeps running acquiring data and performing actions)
Then, the service should be terminated either when the user clicks the stop button or either when the MainActivity is terminated.
Thank you!
i guess you should read more about background services and broadcast in android API 26 or higher
From the official documentation available here
If an app registers to receive broadcasts, the app's receiver consumes resources every time the broadcast is sent. This can cause problems if too many apps register to receive broadcasts based on system events; a system event that triggers a broadcast can cause all of those apps to consume resources in rapid succession, impairing the user experience. To mitigate this problem, Android 7.0 (API level 24) placed limitations on broadcasts, as described in Background Optimization. Android 8.0 (API level 26) makes these limitations more stringent.
Apps that target Android 8.0 or higher can no longer register
broadcast receivers for implicit broadcasts in their manifest. An
implicit broadcast is a broadcast that does not target that app
specifically. For example, ACTION_PACKAGE_REPLACED is an implicit
broadcast, since it is sent to all registered listeners, letting them
know that some package on the device was replaced. However,
ACTION_MY_PACKAGE_REPLACED is not an implicit broadcast, since it is
sent only to the app whose package was replaced, no matter how many
other apps have registered listeners for that broadcast. Apps can
continue to register for explicit broadcasts in their manifests. Apps
can use Context.registerReceiver() at runtime to register a receiver
for any broadcast, whether implicit or explicit.
Broadcasts that require a signature permission are exempted from this restriction, since these broadcasts are only sent to apps that are signed with the same certificate, not to all the apps on the device
you should work with JobScheduler
Important Update
to answer your question in comment : use WorkManager for deferrable background tasks.
This library is backward compatible
It use JobScheduler,FirebaseJobDispatcher or AlarmManager
No Need to depend on play service library.
Recommended by Google for deferrable background work.
Can use features like chaining, constraints etc.
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" />
I'd like to notify my Activity of any Wifi connection changes using the BroadcastReceiver. Since this broadcast is within the application I'm trying to use the more efficient LocalBroadcastManager object.
However no matter what I do, the BroadcastReceiver.onReceive() method will not fire. I may have wired it up incorrectly, or perhaps the WifiManager.NETWORK_STATE_CHANGED_ACTION action I'm listening for cannot be registered against a LocalBroadcastManager? Any help or clarification would be appreciated.
Here's a sample of my Activity class which contains all the logic.
public class MyActivity extends ActionBarActivity {
private BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION))
{
// Do something
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
IntentFilter wifiStatusIntentFilter = new IntentFilter();
wifiStatusIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
wifiStatusIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
LocalBroadcastManager.getInstance(this).registerReceiver(wifiReceiver, wifiStatusIntentFilter);
}
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(wifiReceiver);
}
protected void onResume() {
super.onResume();
IntentFilter wifiStatusIntentFilter = new IntentFilter();
wifiStatusIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
LocalBroadcastManager.getInstance(this).registerReceiver(wifiReceiver, wifiStatusIntentFilter);
}
}
When I switch the wifi on my mobile on and off, or enter and leave the wifi range, the onReceive() method is never fired.
You can't receive WifiManager.NETWORK_STATE_CHANGED_ACTION with LocalBroadcastManager. LocalBroadcastManager works only within your process.
Helper to register for and send broadcasts of Intents to local objects
within your process. This is has a number of advantages over sending
global broadcasts with sendBroadcast(Intent):
You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes
they can exploit.
It is more efficient than sending a global broadcast through the system.
You should use registerReceiver of Context
Since this broadcast is within the application I'm trying to use the more efficient LocalBroadcastManager object.
That only works for broadcasts that you send via LocalBroadcastManager. It does not work for system broadcasts, particularly those sent by other processes.
perhaps the WifiManager.NETWORK_STATE_CHANGED_ACTION action I'm listening for cannot be registered against a LocalBroadcastManager?
Correct.
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!)
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.