Android KitKat 4.4 kills my Service stared by Alarm Manager - android

I have a big problem with Android KitKat and Alarm Manager.
All my apps work with a service that always run in background without Android kill it.
Before Android 4.4 KitKat the solution I found was to start the service through a BroadcastReceiver triggered by an AlarmManager.
...
Intent intent = new Intent(c, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(c, 0, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager am = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pendingIntent);
} else {
setAlarmFromKitkat(am, System.currentTimeMillis(), pendingIntent);
}
...
#TargetApi(19)
private static void setAlarmFromKitkat(AlarmManager am, long ms, PendingIntent pi){
am.setExact(AlarmManager.RTC_WAKEUP, ms, pi);
}
...
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, MyService.class);
context.startService(service);
}
}
On Android 4.4 KitKat by this solution I can start service but after some time Android kill it!
Is there a way to have a Service that works in background without Android 4.4 KitKat kill it?
Many Thanks

For Android Kitkat version
If your app uses AlarmManager...
When you set your app's targetSdkVersion to "19" or higher, alarms that you create using either set() or setRepeating() will be inexact.
To improve power efficiency, Android now batches together alarms from all apps that occur at reasonably similar times so the system wakes the device once instead of several times to handle each alarm.
If your alarm is not associated with an exact clock time, but it's still important that your alarm be invoked during a specific time range (such as between 2pm and 4pm), then you can use the new setWindow() method, which accepts an "earliest" time for the alarm and a "window" of time following the earliest time within which the system should invoke the alarm.
If your alarm must be pinned to an exact clock time (such as for a calendar event reminder), then you can use the new setExact() method.
This inexact batching behavior applies only to updated apps. If you've set the targetSdkVersion to "18" or lower, your alarms will continue behave as they have on previous versions when running on Android 4.4.
Original Source:
http://developer.android.com/about/versions/android-4.4.html

in kitkat,use the code snippet below to restart te service automatically:
#Override
public void onTaskRemoved(Intent rootIntent) {
// TODO Auto-generated method stub
Intent restartService = new Intent(getApplicationContext(),
this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +1000, restartServicePI);
}

Related

Repeat alarm every 20 minutes in android

I'd like to repeat an alarm every 20 minutes.
So I tried:
manifest:
<receiver android:name=".AlarmReceiver" />
AlarmReceiver.class
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent intent2 = new Intent(context, MainActivity.class);
showNotification(context, "text", "text", intent2);
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(context, notification);
r.play();
}
#TargetApi(Build.VERSION_CODES.N)
public void showNotification(Context context, String title, String body, Intent intent) {
...
}
}
and in my main activity:
AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, 1200);
alarmMgr.set(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(), pendingIntent);
It is working only the first time. I'd like to keep it repeating even when the app is not opening, any ideas?
Starting with KitKat (API 19), alarms are all inexact, meaning the system will batch alarms around similar times together. If you need exact timing, there are different APIs to call. Further complicating things, starting with Marshmallow, Android introduced the concept of Doze, which further restricts the when/how things can wake up the device. You can still use exact alarms, but need to use the API which allows it during idle (Doze) time: setAndAllowWhileIdle(). Bear in mind that when your alarm fires, you could be in a Doze window and your app will be restricted on what kinds of operations it can perform.
Try below for Repeating alarm on every 20 minutes interval
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(),1000 * 60 * 20, pendingIntent);
Try to use android worker manager since it is working with doze mode as well. https://developer.android.com/reference/androidx/work/PeriodicWorkRequest#min_periodic_interval_millis
https://developer.android.com/reference/androidx/work/PeriodicWorkRequest

Android repeating alarm in exact time-Using iteratively setExact, how to stop?

According to the documentation:
Note: Beginning with API 19 (KITKAT) alarm delivery is inexact...
There are new APIs to support applications which need strict delivery
guarantees; see... setExact(int, long, PendingIntent).
So if I need exact repeating alarm- need to use setExact - sets one time alarm. So I have:
public static void setAlarm(Context context) {
AlarmManager alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarm.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + ALARM_INTERVAL, pendingIntent);
}
public static void cancelAlarm(Context context) {
AlarmManager alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyService.class);
PendingIntent sender = PendingIntent.getService(context, 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarm.cancel(sender);
}
The order of calling is:
User enables alarm via Activity, so the activity calls setAlarm(getApplicationContext()).
The alarm causing some service to run in the background-This is the purpose of the alarm: to run this service in the background in exact interval of time.
Inside onDestroy() of the service I call setAlarm(getApplicationContext()) and in such way I have the effect of repeating the alarm in exact time. So in the end I have repeats not every interval but interval + execution time of the service until onDestroy is called.
The user can from some activity to cancel the alarm via calling cancelAlarm(getApplicationContext()). In this way the repeating effect of the alarm canceled: The service will not be executed, and specifically the onDestroy() method with the call to set new alarm will not be executed, so the chain is ended.
UPDATE: Result of adb shell dumpsys alarm when should run is:
RTC_WAKEUP #0: Alarm{... type 0 when ... code.dev}
tag=*walarm*:code.dev/.MyService
operation=PendingIntent{...: PendingIntentRecord{... code.dev startService}}
...:code.dev +17ms running, 3 wakeups:
*walarm*:code.dev/.MyService
And after cancel:
...:code.dev +359ms running, 14 wakeups:
*walarm*:code.dev/.MyService
Problem: The service keeps waking up periodically, which means the alarm not canceled.

Android AlarmManager setExact() is not exact

I need to plan sheduled task every 10 minutes.
As in Lollipop and higher version setRepeating() is inexact, I use setExact() and (on alarm firing) I set new exact alarm in 10 minutes.
private void setAlarm(long triggerTime, PendingIntent pendingIntent) {
int ALARM_TYPE = AlarmManager.ELAPSED_REALTIME_WAKEUP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(ALARM_TYPE, triggerTime, pendingIntent);
} else {
alarmManager.set(ALARM_TYPE, triggerTime, pendingIntent);
}
}
triggerTime is calculated SystemClock.elapsedRealtime() + 600_000;
When alarm fires, firstly I plan new one, only after that I run my sheduled task.
setAlarm();
mySheduledTask;
I do have WAKE_LOCK permission in my manifest.
When I test this on Android 4 - it works perfect (deviation might be 12-15 milliseconds).
But when I run app on Xiaomi Redmi Note 3 Pro (5.1.1) - deviation can be up to 15 seconds!
For example, I see in my log file: first run was at 1467119934477 (of RTC time), second - at 1467120541683. Difference is 607_206 milliseconds, not 600_000, as it was planned!
What am I missing? What is a way to simulate behaviour of system alarm (it's the most close usecase that can describe my tack)?
PS. I use IntentService for PendingIntent = PendingIntent.getService(context, 0, myIntent, 0);
The OS chooses how the alarms will work, with consideration of the time you've specified. Because of that, when the phone gets into a 'semi-sleep' mode, it won't necessary use the resource at the time you wish it to. Basically, it waits for 'windows' that the OS opens for it, and only then the alarm you want to run will run, that's why you're experiencing time gaps.
This was introduced on Marshmallow OS and will continue on Nougat OS as well, as part of Google trying to improve the device's battery.
Here's the thing, you have 2 options:
Accept the time delays (but maybe consider using JobScheduler which is more recommended and will save you battery).
Use setExactAndAllowWhileIdle which might cause you battery issues (use this carefully, too many alarms will be bad for your battery).
This method isn't repeating, so you have to declare the next job to be run at the service which the pendingIntent opens.
If you choose option 2, here's the start:
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
int ALARM_TYPE = AlarmManager.RTC_WAKEUP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
am.setExactAndAllowWhileIdle(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
am.setExact(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
else
am.set(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
You can call the method from support.v4:
AlarmManagerCompat.setExact(...);
The internal implementation contains checks by sdk version.
Probably a possible workaround could be something like this:
you schedule the Alarm about 1 minute before the expected time, than you use a Handler.postDelayed to cover the remaining time.
Here you can find an example of this kind of implementation.
The activity just set-up the first alarm:
public class MainActivity extends AppCompatActivity {
private static int WAIT_TIME = 60*1000; //1 minute
public static int DELAY_TIME = 10*60*1000; // delay between iterations: 10min
public static String UPDATE_TIME_KEY = "update_time_key";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setAlarm(this,(new Date().getTime())+DELAY_TIME);
}
public static void setAlarm(Context context, long delay) {
long fireDelay = delay-WAIT_TIME;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putLong(UPDATE_TIME_KEY,delay).apply();
Intent startIntent = new Intent(context, UpdateReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, startIntent,PendingIntent.FLAG_UPDATE_CURRENT );
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
int ALARM_TYPE = AlarmManager.RTC;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(ALARM_TYPE, fireDelay, pendingIntent);
} else {
alarmManager.set(ALARM_TYPE, fireDelay, pendingIntent);
}
}
}
than the receiver continues the loop:
public class UpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
Log.e("RECEIVED","RECEIVED");
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
long fireTime = sharedPreferences.getLong(MainActivity.UPDATE_TIME_KEY, (new Date()).getTime());
long fireDelay =(fireTime-(new Date().getTime())>0)?fireTime-(new Date().getTime()):0;
(new Handler()).postDelayed(new Runnable() {
#Override
public void run() {
Log.e("RECEIVED","PERFORMED");
MainActivity.setAlarm(context,(new Date()).getTime()+MainActivity.DELAY_TIME);
}
},fireDelay);
}
}
I hope it helped.
To answer the question on the system alarm...
Android's stock Alarm Clock/Desk Clock app uses a combination of setAlarmClock and setExactAndAllowWhileIdle.
The following code is used to update notifications:
final PendingIntent operation = PendingIntent.getBroadcast(context, 0,
AlarmStateManager.createIndicatorIntent(context), flags);
final AlarmClockInfo info = new AlarmClockInfo(alarmTime, viewIntent);
alarmManager.setAlarmClock(info, operation);
While at the same time the following code is used to schedule the actual alarm:
if (Utils.isMOrLater()) {
// Ensure the alarm fires even if the device is dozing.
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent)
}
The Pending intent set in setExactAndAllowWhileIdle triggers the alarm while setAlarmClock's intent is then simply ignored.
Android Googlesource
From android documentation of AlarmManager
Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
Also while using setExact() :
The alarm will be delivered as nearly as possible to the requested trigger time.
So its still not guaranteed that setExact will be Exact.
You can try use AlarmManager.setAlarmClock maybe it can help you.
Another thing you need to check which type of BroadcastReceiver you are using, it will be better to use WakefulBroadcastReceiver
Btw you need to change logic for work with Alarm Manager for support Android M, you can you something like this:
if(Build.VERSION.SDK_INT < 23){
if(Build.VERSION.SDK_INT >= 19) {
setExact(...);
} else {
set(...);
}
} else {
setExactAndAllowWhileIdle(...);
}

Alarm Manager doesn't trigger on time

I am trying to create app where i need to send location updates every 1,5,10 minutes and so on.
When app is running, it work accurately but when it goes into background/sleep mode it doesn't work accurately.
I tried both the methods setRepeating/setInExactRepeating but none of them work in background mode.
public static void startSensorAlaram(Context ctx, long minutes) {
AlarmManager alarmManager = (AlarmManager) ctx
.getSystemService(Context.ALARM_SERVICE);
// Alarm_Receiver is a broadcast receiver.
Intent intent = new Intent(ctx, Alaram_Receiver.class);
intent.setAction(Utility.SENSOR_ACTION);
PendingIntent pi = PendingIntent.getBroadcast(ctx, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),minutes,pi);
// alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), minutes, pi);
}
public static void stopAlaramSensor(Context ctx) {
Intent intent = new Intent(ctx, Alaram_Receiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 1,
intent, 0);
AlarmManager alarmManager = (AlarmManager) ctx
.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
Alarm Receiver - Broadcast receiver
public class Alaram_Receiver extends WakefulBroadcastReceiver {
private SharedPreferences sp;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
sp = context.getSharedPreferences(Utility.SHARED_PREFS, 0);
if (intent.getAction().equalsIgnoreCase(Utility.SENSOR_ACTION)) {
if (sp.getBoolean("logged_in", false)) {
// context.startService(new Intent(context,SensorService.class));
startWakefulService(context,new Intent(context,SensorService.class));
} else
Utility.stopAlaramSensor(context);
}
}
}
Note:- Min API version is 15 and compile version is 23.
There are two issues.
1) As of Android API >= 19, you should use new AlarmManager.setExact() method instead of set() or setRepeating(). Here is the quote from official document.
Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will
shift alarms in order to minimize wakeups and battery use. There are
new APIs to support applications which need strict delivery
guarantees; see setWindow(int, long, long, PendingIntent) and
setExact(int, long, PendingIntent).
2) As of Android 6.0, there is a deep sleep mode introduced, called Doze.
It is designed to reduce battery consumption when device is being standby. There are so many restriction and what you could do in that mode is very limited. You need to use the new AlarmManager.setExactAndAllowWhileIdle() to make Alarm fired in Doze mode at your preferred time.
More information about Doze mode is available here Optimizing for Doze and App Standby

How to keep a service alive using AlarmManager.setInexactRepeating()?

I have some existing code that spawns a service intent which does a bunch of stuff in the background. This code does work...
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(serviceIntent);
My question is: how to change this to use the AlarmManager.setInexactRepeating(...) methods?
I have changed the above code to this:
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.putExtra("STARTED_BY", starter);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Set up recurring alarm that restarts our service if
// it crashes or if it gets killed by the Android OS
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getService(context, 0, serviceIntent, 0);
//am.cancel(pi);
am.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP //wake up the phone if it's asleep
, cal.getTimeInMillis()
, 10000
, pi);
And I have added these permissions to AndroidManifest.xml...
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="com.android.alarm.permission.WAKE_LOCK"/>
My understanding is that this is supposed to start the service immediately and then try to restart it again every 10 seconds. But this code isn't working properly.
Using this new code, the service never starts at all and I cannot see why not. To complicate matters the debugger never seems to attach to the app in time to see what's going on.
Can anyone see what I'm doing wrong?
Put AlarmManager code under onDestroy() function of service to schedule start of service as below:
#Override
public void onDestroy() {
/**
* Flag to restart service if killed.
* This flag specify the time which is ued by
* alarm manager to fire action.
*/
final int TIME_TO_INVOKE = 5 * 1000; // try to re-start service in 5 seconds.
// get alarm manager
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AutoStartServiceReceiver.class);
PendingIntent pendingIntent = PendingIntent
.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// set repeating alarm.
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() +
TIME_TO_INVOKE, TIME_TO_INVOKE, pendingIntent);
}
And handle starting of your service in AutoStartServiceReceiver as below:
public class AutoStartServiceReceiver extends BroadcastReceiver {
private static final String TAG = AutoStartServiceReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
// check broadcast action whether action was
// boot completed or it was alarm action.
if (intent.getAction().equals(AppConstants.ACTION_ALARM_INTENT)) {
context.startActivity(new Intent(context, YourActivity.class));
// handle service restart event
LockerServiceHelper.handleServiceRestart(context);
}
}
}
Kindly note that, your service will not restart if you stop it manually from settings-apps-running apps-your app.
Your service is not starting because of AlarmManager.ELAPSED_REALTIME_WAKEUP, while it should be using AlarmManager.RTC_WAKEUP
If you want to run every 10s keep in mind that above API 21 alarm intervals below 60s are rounded up to 60s.
Also, consider using WakefulIntentService
https://github.com/commonsguy/cwac-wakeful

Categories

Resources