I have some sensitive data that I load on the device in my application. And it can be reused between sessions / multiple uses of the app and be cleared when not in use / open / active.
So the app is not active / running. There is a notification showing that the sensitive information is still in memory.
But I want to give the user an option of clearing this data after a set amount of time.
So my question is how do I run some code after a set number of minutes?
You can schedule an alarm using the AlarmManager and run your code from the BroadcastReceiver/Service. The alarm will trigger weather your app is dead or alive. (If it's dead it will be awaken)
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 0);
You can use this method to register a one time alarm. So if you want the alarm to trigger in 5 mins, the delayMillis value should be 5 * 1000 * 60
void registerOneTimeAlarm(PendingIntent pendingIntent, long delayMillis) {
int SDK_INT = Build.VERSION.SDK_INT;
long timeInMillis = System.currentTimeMillis() + delayMillis;
if (SDK_INT < Build.VERSION_CODES.KITKAT) {
alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
} else if (SDK_INT < Build.VERSION_CODES.M) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
} else {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
}
}
Let's say you will use a receiver, it will look like this:
public class AlarmReceiver
extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Your code here
}
}
And don't forget to add it in the Manifest:
<receiver android:name="your.package.AlarmReceiver"/>
IMPORTANT
Registered alarms are cleared when the device reboots, you need to re-register the alarm after the device boots. (If this is what you really need)
You can try this
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Actions to be performed
}
}, TIME_OF_DELAY);
Related
I have an android application with pjsua2 open source project.Its working fine both incoming and outgoing call.But when android goes in deep sleep sip registration not working.I am using service for registration.But its not reregistering..
accCfg.getRegConfig().setRetryIntervalSec(600);
accCfg.getRegConfig().setFirstRetryIntervalSec(15);
any help would be appreciated.
For when android goes in deep sleep android os kills almost all services and other background tasks. So for keeping sip registration I believe AlarmManager will be a very good option.Sample example will awake ur application in every 5 min..so re-register that time...
public void setAlarmManagerFroDeepSleep() {
int interval = 1000 * 60 * 5;
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
/* Repeating on every 5 minutes interval */
manager.setRepeating(AlarmManager.RTC_WAKEUP, interval,
interval, pendingIntent);
}
Here is sample AlarmReciever class..
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("Alerm Manager", "I'm running");
if(Connectivity.isConnected(context)) {
try {
//make re-register here....
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
after reading all the QA i didnt get any proper solution.
I have 2 problems
1. Alarm fires twice even if i register my receiver only in manifest.(not by code)
2. when i update interval time of alarm it gets fires randomly
here is my method for set alarm
public void AlarmCall(int min) {
Intent intent = new Intent(context, AlarmReceiver.class);
PendingIntent pintent = PendingIntent.getBroadcast(context,0 , intent, 0);
alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
cancelAlarm(alarm,pintent);
if(Build.VERSION.SDK_INT<18) {
alarm.set(AlarmManager.RTC_WAKEUP, 1000 * 60 * min, pintent);
}
else if(Build.VERSION.SDK_INT>=19 && Build.VERSION.SDK_INT<=22)
{ alarm.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(), 1000*60*min, pintent);
}
else if(Build.VERSION.SDK_INT>=23)
{ alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,1000*60*min,pintent);
}
}
method to cancel alarm :
public void cancelAlarm(AlarmManager alarm,PendingIntent p)
{
alarm.cancel(p);
Log.d("Alarm","Alarm Cancle");
}
in my project Application class i have to start alarm with 10 min time interval and it works fine , according to user input value i need to update time interval.
so i call this method with int min input value and cancel first alarm.
but in marshmallow it fires at every 5 seconds, and kitkat lollipop it fires randmoly.
even checked with setExact() method
I had the same issue, use setWindow solved the problem
long repeatInterval = 1000 * 60 * min;
long triggerTime = SystemClock.elapsedRealtime()
+ repeatInterval;
AlarmManager alarms = (AlarmManager) this.getSystemService(
Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= 19)
{
alarms.setWindow(AlarmManager.RTC_WAKEUP,
triggerTime,
repeatInterval,
pendingIntent);
}else{
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP,
triggerTime,
repeatInterval,
pendingIntent);}
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(...);
}
I am trying to create a simple alarm application where alarm should go off on several days.
I have created a service which registers alarm manager based on data saved in preferences. Everything is working fine except that it does not trigger alarm when app is not running.
AlarmService.java
#Override
protected void onHandleIntent(Intent intent) {
Toast.makeText(this,"Service started",Toast.LENGTH_LONG);
//set separate pending intent values for all days
setIntents();
// register the alarm manager here
alarmManager = (AlarmManager)(getSystemService(Context.ALARM_SERVICE));
execute();
}
private void execute() {
prefs=getSharedPreferences("user_data_pref",Context.MODE_PRIVATE);
prefEditor=prefs.edit();
int hour=prefs.getInt("ALARM_HOUR",0);
int minute=prefs.getInt("ALARM_MINUTE",0);
if(prefs.getString("ALARM_STATUS","NOT_SET").equals("SET"))
isAlarmOn=true;
if(prefs.getString("NOTIFICATION_STATUS","NOT_SET").equals("SET"))
isNotificationOn=true;
if (prefs.getBoolean("MON_SET",false)) {
setAlarmFor(Calendar.MONDAY,hour,minute,mondayIntent,isAlarmOn,isNotificationOn);
}
if (prefs.getBoolean("TUE_SET",false)) {
setAlarmFor(Calendar.TUESDAY,hour,minute,tuesdayIntent,isAlarmOn,isNotificationOn);
}
if (prefs.getBoolean("WED_SET",false)) {
setAlarmFor(Calendar.WEDNESDAY,hour,minute,wednesdayIntent,isAlarmOn,isNotificationOn);
}
if (prefs.getBoolean("THU_SET",false)) {
setAlarmFor(Calendar.THURSDAY,hour,minute,thursdayIntent,isAlarmOn,isNotificationOn);
}
if (prefs.getBoolean("FRI_SET",false)) {
setAlarmFor(Calendar.FRIDAY,hour,minute,fridayIntent,isAlarmOn,isNotificationOn);
}
if (prefs.getBoolean("SAT_SET",false)) {
setAlarmFor(Calendar.SATURDAY,hour,minute,saturdayIntent,isAlarmOn,isNotificationOn);
}
if (prefs.getBoolean("SUN_SET",false)) {
setAlarmFor(Calendar.SUNDAY,hour,minute,sundayIntent,isAlarmOn,isNotificationOn);
}
}
private void setAlarmFor(int weekday, int hour, int minute, PendingIntent pendingIntent, boolean isAlarmOn, boolean isNotificationOn){
if(isAlarmOn){
prefEditor.putString("ALARM_STATUS","SET");
prefEditor.commit();
}
if(isNotificationOn){
prefEditor.putString("NOTIFICATION_STATUS","SET");
prefEditor.commit();
}
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_WEEK,weekday);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND,0);
/* Repeating on every week interval */
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
7*24*60*60*1000, pendingIntent);
}
private void setIntents(){
Intent intent = new Intent(this,AlarmReceiver.class);
mondayIntent=PendingIntent.getBroadcast(this, Calendar.MONDAY, intent, 0);
tuesdayIntent=PendingIntent.getBroadcast(this, Calendar.TUESDAY, intent, 0);
wednesdayIntent=PendingIntent.getBroadcast(this, Calendar.WEDNESDAY, intent, 0);
thursdayIntent=PendingIntent.getBroadcast(this, Calendar.THURSDAY, intent, 0);
fridayIntent=PendingIntent.getBroadcast(this, Calendar.FRIDAY, intent, 0);
saturdayIntent=PendingIntent.getBroadcast(this, Calendar.SATURDAY, intent, 0);
sundayIntent=PendingIntent.getBroadcast(this, Calendar.SUNDAY, intent, 0);
}
It should trigger AlarmReceiver class that displays alarm.
AlarmReceiver.java
#Override
public void onReceive(Context context, Intent intent) {
prefs=context.getSharedPreferences("user_data_pref",Context.MODE_PRIVATE);
//Get Status of currently set alarm and notification
alarmStatus=prefs.getString("ALARM_STATUS","NOT_SET");
notificationStatus=prefs.getString("NOTIFICATION_STATUS","NOT_SET");
//Send notification if the notification is set
if(notificationStatus.equals("SET")){
Toast.makeText(context, "NOTIFICATION START SUCCESSFUL", Toast.LENGTH_SHORT).show();
playNotificationSound(context,getNotificationSound());
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setContentTitle("") // title for notification
.setContentText("") // message for notification
.setAutoCancel(true); // clear notification after click
Intent intent_1 = new Intent(context, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(context, 11, intent_1, Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, mBuilder.build());
displayNotification(context);
}
//Fire Alarm if the Alarm is set
if(alarmStatus.equals("SET")){
Toast.makeText(context, "ALARM START SUCCESSFUL", Toast.LENGTH_SHORT).show();
/** Creating Alarm */
Intent i = new Intent(context,FitterfoxAlarmActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
AndroidManifest.xml
<receiver android:name=".OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".AlarmReceiver"></receiver>
<service android:name=".AlarmService"></service>
Try this set of code your service never going to stop it's works for me. "alarmManager.setRepeating" not working in many of the lollipop and marshmallows device once it started then lose control over the service this will help you out.
if (android.os.Build.VERSION.SDK_INT >= 23) {
piLR = PendingIntent.getBroadcast(context, 0, intentLR,
PendingIntent.FLAG_UPDATE_CURRENT);
amLR.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
interval, piLR);
} else if (android.os.Build.VERSION.SDK_INT >= 19
&& android.os.Build.VERSION.SDK_INT < 23) {
piLR = PendingIntent.getBroadcast(context, 0, intentLR,
PendingIntent.FLAG_UPDATE_CURRENT);
amLR.setInexactRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), interval, piLR);
} else {
amLR.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), interval, piLR);
}
Starting with KitKat, changes were made to the AlarmManager to improve device battery life. The time at which alarms fire is less exact. If you want your alarms to fire within minutes of the scheduled time, you will need to change the way you are using AlarmManager.
For KitKat (API 19) and later builds, the times specified in setRepeating() are inexact, as described in the documentation. The docs also note:
If your application needs precise delivery times then it must use
one-time exact alarms, rescheduling each time.
You indicate that the alarms do not fire when the app is not running. Because the period for your alarms is one week, there can be a large difference between the scheduled time and the actual time the alarm is fired. The documentation indicates:
Your alarm's first trigger will not be before the requested time, but
it might not occur for almost a full interval after that time.
I suspect that with your current code, you would eventually see the alarm fire, but might have to wait a very long time after the expected time.
Your revised code will probably need to use setWindow().
Marshmallow introduced Doze mode, which further changes alarm scheduling. As #Andy noted in his answer, to get the beavior you want on Marshmallow and later devices, you will need to use setExactAndAllowWhileIdle().
Two other things that need to be fixed. Your PendingIntents should probably use flag PendingIntent.FLAG_UPDATE_CURRENT. In your receiver, the flag Intent.FLAG_ACTIVITY_NEW_TASK belongs on intent_1, not pi.
I have this method which should schedule alarms but when the time arrives it doesn't start the pendingintent ??
public void setAlarm(String name, long time) {
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent dialog = new Intent(this, SubActivity.class);
dialog.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(this, 0, dialog, 0);
if (Build.VERSION.SDK_INT >= 19) {
if (System.currentTimeMillis() < time) {
am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi);
}else{
time+=(AlarmManager.INTERVAL_DAY*7);
am.setExact(AlarmManager.RTC_WAKEUP, time, pi);
}
} else {
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, AlarmManager.INTERVAL_DAY * 7, pi);
}
}
The problem is you are using a PendingIntent for an Activity, which will not necessarily keep the device awake long enough for the Activity to get started. You'll have to use a PendingIntent for a BroadcastReceiver which leverages a wake lock to keep the device awake until your app code can run. WakefulBrodcastReceiver is a good choice, or you can roll your own as needed. See this article for an explanation and sample of how to use alarms to wake the device: http://po.st/7UpipA