Timer not expiring precisely during sleep state in Android - android

We are trying to implement timer for native code in android .The timer should work precisely during wake and sleep mode .When timer expires then our native code will send DPD(Dead peer detection) messages to the network
We tried following approaches .
Using android framework API's for alarm manager in userspace code and below are the results:
This doesn't give the accurate results even during wake state for small timers like 2s,3s,5s.
Does not work precisely for sleep mode also.
We tried to use kernel timer in kernel space code and below are the results:
Works perfectly for wake state.
But for sleep state timers do not expire at all.When we wake the device up manually then the timers get expire .So,in conclusion kernel timers do not work during sleep state.
3.Using wake lock
*We are trying to avoid use of wake lock as it may cause significant performance issues
P.S - Open source Ipsec implementation strongswan sends the DPD messages precise time even during sleep mode .But it seems that strongswan does not use wake lock ,so we are still trying to figure out how it works during sleep mode.Anybody searching for answer to this question might want to look into that code.
Can anyone please suggest something to resolve this issue.

When Android goes to sleep it will have several states, the last one is freezing all the processes and turning off the CPU.
In that case your times are not going to fire. You must create an event that will wake up the kernel and set a wake lock so the cpu will not turn off again. This can be done using android alarms.

The only way to have the timer work precisely in sleep mode is to keep device partially awake using Wakelock. But make sure your application really need the timer to work all the time, because the document says,
Device battery life will be significantly affected by the use of this API. Do not acquire PowerManager.WakeLocks unless you really need them, use the minimum levels possible, and be sure to release them as soon as possible.
Go through PowerManager Class, use the below code to acquire partial wake lock
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
..cpu will stay on during this section..
wl.release();

Initiate timer and when app goes background then start alarmManager. Again if app comes foreground and timer is not expired then it will re trigger the timer and will remove the alarm manager.
private int timeToStart;
private TimerState timerState;
private static final int MAX_TIME = 60; //Time length is 60 seconds
private enum TimerState {
STOPPED,
RUNNING
}
private void initTimer() {
Log.e(TAG,"initTimer called");
long startTime = mPrefs.getStartedTime(); //here mprefs is your shared preference manager
if (startTime > 0) {
timeToStart = (int) (MAX_TIME - (getNow() - startTime));
if (timeToStart <= 0) {
// TIMER EXPIRED
onTimerFinish();
} else {
startTimer();
timerState = TimerState.RUNNING;
}
} else {
timeToStart = MAX_TIME;
timerState = TimerState.STOPPED;
}
}
private long getNow() {
Calendar rightNow = Calendar.getInstance();
return rightNow.getTimeInMillis() / 1000;
}
private void onTimerFinish() {
Log.e(TAG,"onTimerFinish() called");
timerState = TimerState.STOPPED;
mPrefs.setStartedTime(0);
timeToStart = MAX_TIME;
}
private void startTimer() {
Log.e(TAG,"startTimer() called");
countDownTimer = new CountDownTimer(timeToStart * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timeToStart -= 1;
}
#Override
public void onFinish() {
onTimerFinish();
}
}.start();
}
public void setAlarmManager() {
int wakeUpTime = (mPrefs.getStartedTime() + MAX_TIME) * 1000;
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, TimeReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
am.setAlarmClock(new AlarmManager.AlarmClockInfo(wakeUpTime, sender), sender);
} else {
am.set(AlarmManager.RTC_WAKEUP, wakeUpTime, sender);
}
}
public void removeAlarmManager() {
Intent intent = new Intent(this, TimeReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.cancel(sender);
}
#Override
protected void onResume() {
super.onResume();
initTimer();
removeAlarmManager();
}

Related

Pausing Service during alarm, resuming it afterwards

So I have a Service that I want to be able to listen for Alarms and temporarily shut itself down/pause whilst the alarm rings, and then resume itself afterwards. What my Service does is that it inflates a view using WindowManager on top of the screen - it's a lock screen app.. But as such, it's always on top of everything else..
This was easy enough to implement for incoming calls using a PhoneStateListener but I haven't seen anything as handy for alarms - I guess I could implement an AlarmManager.onAlarmListener that shuts my service down once the alarm rings, but I'm not sure of how I would turn it back on again afterwards.
Thankful for any help!
Finally figured it out!
You can get the time of the next alarm like so:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.getNextAlarmClock().getTriggerTime()
So just add this to your service onCreate method:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
if (alarmManager.getNextAlarmClock() != null) {
UIHandler.postAtTime(new Runnable() {
#Override
public void run() {
stopSelf();
}
}, alarmManager.getNextAlarmClock().getTriggerTime());
}
Essentially what it does is to get the time of your next alarm in milliseconds, then post a runnable at the time of the next alarm.
I believe it will only work on API 21+
Maybe, you can try to implement AudioManager.OnAudioFocusChangeListener
https://developer.android.com/reference/android/media/AudioManager.OnAudioFocusChangeListener.html
#Override
public void onAudioFocusChange(int i) {
if (i <= 0 && i != -3) {
// pause
} else if (i > 0) {
// resume
}
}
}

Android Wear Watch Face Vibrate With Screen Off

I have an Android Wear watch face that I'm trying to have vibrate the watch on the hour. It is working except in cases where the watch screen is off. According to the log statements, the handler method is called every minute and the chime method is called on the hour. If I'm debugging over bluetooth with the Moto 360, it works even with the screen off. If I install a release apk, it only vibrates if the screen is on. If the screen is off at the top of the hour, it wont vibrate until the screen comes back on. I have tried acquiring a wake lock before the vibrate with no luck. I'm thinking it may work if I acquire a wake lock in the onCreate and release it in the onDestroy but I would rather not do that to preserve battery. Another interesting tidbit is that I have another function that vibrates when certain data changes in the wearable data api and that is working with the screen off. Maybe the WearableListenerService wakes the watch up long enough for the vibrate to occur. Is there something wrong with my logic or is this a limitation of certain Android Wear devices?
Time change handler:
final Handler mUpdateTimeHandler = new Handler() {
#Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_UPDATE_TIME:
MyLog.d("Time Tick Message Handler");
doTimeTickStuff();
long timeMs = System.currentTimeMillis();
long delayMs = mInteractiveUpdateRateMs - (timeMs % mInteractiveUpdateRateMs);
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
break;
}
}
};
doTimeTickStuff()
private void doTimeTickStuff()
{
MyLog.d("timetickstuff");
try {
mCalendar = Calendar.getInstance();
int currMin = mCalendar.get(Calendar.MINUTE);
if (currMin == 0) {
hourlyChime();
}
}
catch(Exception ex)
{
MyLog.e(ex, "Error occurred in time tick handler");
}
if (mIsVisible) {
invalidate();
}
}
hourlyChime()
private void hourlyChime(){
Vibrator v = (Vibrator) getBaseContext().getSystemService(VIBRATOR_SERVICE);
if (v.hasVibrator()) {
MyLog.d("vibrating");
v.vibrate(1000);
}
else {
MyLog.d("No vibrator");
}
}
Update
The solution that worked was to create an AlarmManager and register it with a broadcast receiver in the watch face onCreate then unregister the receiver in onDestroy
onCreate()
#Override
public void onCreate(SurfaceHolder holder) {
super.onCreate(holder);
mChimeAlarmManager =
(AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent ambientStateIntent = new Intent("packagename.HOURLY_CHIME");
mChimePendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
1234, ambientStateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
WeatherTime.this.registerReceiver(chimeReceiver,
new IntentFilter("packagename.HOURLY_CHIME"));
long alarmMs = getMsTillNextHour() + System.currentTimeMillis();
mChimeAlarmManager.setExact(
AlarmManager.RTC_WAKEUP,
alarmMs,
mChimePendingIntent);
}
Broadcast Receiver
private BroadcastReceiver chimeReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent) {
hourlyChime();
mChimeAlarmManager.setExact(
AlarmManager.RTC_WAKEUP,
getMsTillNextHour() + System.currentTimeMillis(),
mChimePendingIntent);
}
};
onDestroy()
#Override
public void onDestroy() {
mChimeAlarmManager.cancel(mChimePendingIntent);
super.onDestroy();
}
When the watch goes into ambient mode, it goes into a deep sleep. As a result, code written with Handler will not run. As a result, you should use AlarmManager. For details on how to implement this, you should refer to the "Update more frequently" section on this page about the always-on functionality of Android Wear.
With regards to Bluetooth debug mode, I suspect that it works because the watch never goes into deep sleep. The same happens when I develop apps while the watch is docked.
Lastly, as for the wake up frequency, I think your functionality is fine as it only fires once an hour. For others reading this, please refrain from waking the watch up more than once a minute as this will severely impact battery life. Always test your watch face for battery life before uploading to the Play Store.
in my project i use Alarm manager with MyIntentService extends IntentService.
To wake up (on screen) device in onHandleIntent
use following:
if (intent.getAction() != null) {
tmp = intent.getAction();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock((PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), TAG);
wakeLock.setReferenceCounted(true);
if(!wakeLock.isHeld()) {
wakeLock.acquire();
}
}

Service pauses on screen lock

For testing purposes i have made a service that beeps
every 1 minute. (No client-server interface yet). It beeps okay when
the screen in on, but when it goes to sleep the beeping stops.
I am making an application that has to periodically poll the a server
for something.
For this, I am trying to create a service that'll constantly be
running in the background, poll the server every 1 min and then based
on the reply from server it shall generate a task bar notification.
I have a test activity with two buttons, 1 to start and the other to
stop the service. And one service class named S_PS_PollService
The setOnClickListener of 'Start Activity' button contains:
Thread pollServiceThread = new Thread() {
public void run() {
startService(new Intent(MM_MainMenu.this,
S_PS_PollService.class));
}
};
pollServiceThread.start();
The 'Stop Activity' button simply has:
stopService(new Intent(MM_MainMenu.this, S_PS_PollService.class));
Following are the methods from S_PS_PollService class:
public void onCreate() {
pollSound = MediaPlayer.create(this, R.raw.chirp);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent myIntent = new Intent(this, S_PS_PollService.class);
pendingIntent = PendingIntent.getService(this, 0, myIntent, 0);
// for wake lock
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag")
// for calendar
calendar = Calendar.getInstance();
}
Onstart:
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
wl.acquire();
pollSound.start();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.MILLISECOND, 60000);
alarmManager.set(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), pendingIntent);
wl.release();
}
Whenever the alarm kicks off onStart() method is executed, making the
beep and setting new alarm. But it works only as long as screen is on.
I have tried for https://github.com/commonsguy/cwac-wakeful but didnt
get it. Relatively new to android ...
Please help me, im very desperate :) Thanks, !
You have to use the AlarmManager, there are plenty of posts here on stackoverflow.
You want to acquire a partial wake lock (leaving the CPU running whenever sleep is entered on the device) as suggested by your code.
The issue is your presumably overriden on start releases the wake lock. You want to release your wakeLock in onDestroy .. once your service is finished running.
This finally worked for me.
Download the CWAC-WakefulIntentService.jar from https://github.com/commonsguy/cwac-wakeful
add a class in your project
import com.commonsware.cwac.wakeful.WakefulIntentService;
public class WakeService extends WakefulIntentService {
public WakeService(String name) {
super(name);
}
#Override
protected void doWakefulWork(Intent intent) {
}
}
now add the following line in your code where ever you want to repeat the loop and wake the device up
WakefulIntentService.sendWakefulWork(this, S_WS_WakeService.class);

Running a repeating task in background on a real time application

I'm writing an application which is continuously listening and checking the sensors (almost all available) and saving that data into the database in the device.
I need to make some calculations every X second with that data and throw a new event if the calculations check says so.
I'm thinking about requesting to have the device plugged in while using the application (regarding battery drain).
What's the best approach for the task that needs to make the calculations and throw the event? Timer? Threads? AsynkTask? AlarmManager? Another approach?
I want to keep getting sensors data and saving them to the database despite if the application is not on foreground...it should save the values as long as the application is not stopped by the user.
One option for that is wake locks (PARTIAL_WAKE_LOCK, which keeps CPU running).
I'd like to hear different opinions.
Thanks in advance! Guillermo.
You can use AlarmManager to setup the repeating tasks (this is the Android prefered way of setting future/repeating tasks). To make the calculations use a Service (if you think calculations are going to be expensive, then think about moving them to a separate worker thread or use IntentService).
Regarding the wake lock (from the AlarmManager reference):
The Alarm Manager holds a CPU wake
lock as long as the alarm receiver's
onReceive() method is executing. This
guarantees that the phone will not
sleep until you have finished handling
the broadcast. Once onReceive()
returns, the Alarm Manager releases
this wake lock. This means that the
phone will in some cases sleep as soon
as your onReceive() method completes.
If your alarm receiver called
Context.startService(), it is possible
that the phone will sleep before the
requested service is launched. To
prevent this, your BroadcastReceiver
and Service will need to implement a
separate wake lock policy to ensure
that the phone continues running until
the service becomes available.
This is a modified snippet of a service I wrote to log CPU frequency some time ago. It lacks the Application and the Activity part, but illustrates how I wrote the Service to keep logging every ten seconds. It does not log when the phone goes into deep sleep, so if you want to log without interruptions, then you will need to acquire PARTIAL_WAKE_LOCKs, but consider that battery life will be severely reduced by that.
public class YOURCLASS_Service extends Service {
private long mStartTime = 0L;
private final Handler mHandler = new Handler();
private Runnable mUpdateTimeTask;
private YOURAPP app;
#Override
public void onCreate() {
super.onCreate();
app = (YOURAPP) getApplicationContext();
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service finished.", Toast.LENGTH_SHORT).show();
stopLog ();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (app.isRunning())
return START_STICKY;
try {
File file = new File(Environment.getExternalStorageDirectory(), "yourlog.csv");
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file, false));
out.write("Log title");
out.close();
} catch (java.io.IOException e) {
stopLog ();
Toast.makeText(this, "Error creating log file. Aborting.", Toast.LENGTH_SHORT).show();
}
mUpdateTimeTask = new Runnable() {
public void run() {
long millis = SystemClock.uptimeMillis() - mStartTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
readYourSensors ();
if (!writeLog (str)) stopLog();
mHandler.postAtTime(this, mStartTime + (((minutes * 60) + seconds + 10) * 1000));
mHandler.postDelayed (mUpdateTimeTask, 10000);
}};
mStartTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
Notification notification = new Notification(R.drawable.notification_icon, "App title", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, YOURCLASS.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), "App title", "Please see /sdcard/yourlog.csv", contentIntent);
startForeground(startId, notification);
app.isRunning(true);
return START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
public void stopLog () {
mHandler.removeCallbacks(mUpdateTimeTask);
}
}

Running a repeating task in background on a real time application [duplicate]

I'm writing an application which is continuously listening and checking the sensors (almost all available) and saving that data into the database in the device.
I need to make some calculations every X second with that data and throw a new event if the calculations check says so.
I'm thinking about requesting to have the device plugged in while using the application (regarding battery drain).
What's the best approach for the task that needs to make the calculations and throw the event? Timer? Threads? AsynkTask? AlarmManager? Another approach?
I want to keep getting sensors data and saving them to the database despite if the application is not on foreground...it should save the values as long as the application is not stopped by the user.
One option for that is wake locks (PARTIAL_WAKE_LOCK, which keeps CPU running).
I'd like to hear different opinions.
Thanks in advance! Guillermo.
You can use AlarmManager to setup the repeating tasks (this is the Android prefered way of setting future/repeating tasks). To make the calculations use a Service (if you think calculations are going to be expensive, then think about moving them to a separate worker thread or use IntentService).
Regarding the wake lock (from the AlarmManager reference):
The Alarm Manager holds a CPU wake
lock as long as the alarm receiver's
onReceive() method is executing. This
guarantees that the phone will not
sleep until you have finished handling
the broadcast. Once onReceive()
returns, the Alarm Manager releases
this wake lock. This means that the
phone will in some cases sleep as soon
as your onReceive() method completes.
If your alarm receiver called
Context.startService(), it is possible
that the phone will sleep before the
requested service is launched. To
prevent this, your BroadcastReceiver
and Service will need to implement a
separate wake lock policy to ensure
that the phone continues running until
the service becomes available.
This is a modified snippet of a service I wrote to log CPU frequency some time ago. It lacks the Application and the Activity part, but illustrates how I wrote the Service to keep logging every ten seconds. It does not log when the phone goes into deep sleep, so if you want to log without interruptions, then you will need to acquire PARTIAL_WAKE_LOCKs, but consider that battery life will be severely reduced by that.
public class YOURCLASS_Service extends Service {
private long mStartTime = 0L;
private final Handler mHandler = new Handler();
private Runnable mUpdateTimeTask;
private YOURAPP app;
#Override
public void onCreate() {
super.onCreate();
app = (YOURAPP) getApplicationContext();
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service finished.", Toast.LENGTH_SHORT).show();
stopLog ();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (app.isRunning())
return START_STICKY;
try {
File file = new File(Environment.getExternalStorageDirectory(), "yourlog.csv");
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file, false));
out.write("Log title");
out.close();
} catch (java.io.IOException e) {
stopLog ();
Toast.makeText(this, "Error creating log file. Aborting.", Toast.LENGTH_SHORT).show();
}
mUpdateTimeTask = new Runnable() {
public void run() {
long millis = SystemClock.uptimeMillis() - mStartTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
readYourSensors ();
if (!writeLog (str)) stopLog();
mHandler.postAtTime(this, mStartTime + (((minutes * 60) + seconds + 10) * 1000));
mHandler.postDelayed (mUpdateTimeTask, 10000);
}};
mStartTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
Notification notification = new Notification(R.drawable.notification_icon, "App title", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, YOURCLASS.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), "App title", "Please see /sdcard/yourlog.csv", contentIntent);
startForeground(startId, notification);
app.isRunning(true);
return START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
public void stopLog () {
mHandler.removeCallbacks(mUpdateTimeTask);
}
}

Categories

Resources