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
}
}
}
Related
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();
}
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();
}
}
Problem is that onReceive method of BroadcastReceiver usually "called" by AlarmManager is delayed until device is awoken by the user.
This has never happened to me, only information I have was is from the report sent by user. In the log I saw that in the first case onReceive method call was delayed by almost 2 hours and in the second one by about 20 minutes. In both situations alarm (and onReceive) has started just after the phone was awoken by the user.
Problem has occured twice in two consecutive days and user states it has never happened before. Only distinctive change in phone's settings was that Air Mode was enabled.
My code:
Alarm is set like:
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
Logger.log("posting alarm " + id + " for " + formatTime(timeInMillis);
Broadcast Receiver's onReceiveMethod:
#Override
public void onReceive(Context context, Intent intent) {
Logger.initialize(context, "AlarmReceiver");
...
}
Logs received from the user:
481. 20/05 13:00:04 v89: posting alarm 4 for 7:0 (in 17:59)
486. 21/05 08:58:00 v89: logger initialized again from AlarmReceiver
536. 21/05 09:04:54 v89: posting alarm 4 for 7:0 (in 21:55)
541. 22/05 07:22:24 v89: logger initialized again from AlarmReceiver
Is it possible for Air Mode to block phone's awakening somehow? Can I prevent it? Or maybe it is something completely different? Any help is welcomed.
Device is Samsung Galaxy SIII (GT-I9305) with Android 4.1.2
Edit:
Just in case that delay could be somehow caused by the Logger, here's its code. mHandler is created with use of HandlerThread, so I believe it can't block onReceive, right?
public synchronized static void initialize(Context context, String src) {
if (mInstance == null) {//wasn't null
...
} else {
Logger.log("logger initialized again from " + src);
}
}
public synchronized static void log(final String text) {
Log.d(TAG, text);
if (mInstance != null && mInstance.mLoggingEnabled) {
mInstance.mHandler.post(new Runnable() {
#Override
public void run() {
//some database operations
}
});
}
}
If you want to schedule your tasks with the specified interval don't use flags AlarmManager.RTC and AlarmManager.RTC_WAKEUP with method alarm.setRepeating(...). Because in this case alarm will be bounded to the device's real time clock. So changing the system time may cause alarm to misbehave. You must use flags AlarmManager.ELAPSED_REALTIME or AlarmManager.ELAPSED_REALTIME_WAKEUP. In this case SystemClock.elapsedRealtime() will serve as a basis for scheduling an alarm.
The code will look like:
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + checkIntervalMillis, checkIntervalMillis, pendingIntent);
If you want your long running task to be executed when device in the sleep mode I recommend to use WakefulIntentService library by CommonsWare: https://github.com/commonsguy/cwac-wakeful
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);
My android application requires a password to be entered in the first activity. I want to be able to automatically send the application back to the password entry screen after the application has been idle for a fixed amount of time.
The application has multiple activities, but I would like the timeout to be global for all activities. So, it wouldn't be sufficient to create a timer thread in the onPause() method of an Activity.
I'm not sure what the best definition for the application being idle is, but no activities being active would be sufficient.
I know another answer is accepted already, but I came across this working on a similar problem and think I'm going to try an alternate much simpler approach that I figured I may as well document if anyone else wants to try to go down the same path.enter code here
The general idea is just to track the system clock time in a SharedPreference whenever any Activity pauses - sounds simple enough, but alas, there's a security hole if that's all you use, since that clock resets on reboot. To work around that:
Have an Application subclass or shared static singleton class with a global unlocked-since-boot state (initially false). This value should live as long as your Application's process.
Save the system time (realtime since boot) in every relevant Activity's onPause into a SharedPreference if the current app state is unlocked.
If the appwide unlocked-since-boot state is false (clean app start - either the app or the phone restarted), show the lock screen. Otherwise, check the SharedPreference's value at the lockable activity's onResume; if it's nonexistent or greater than the SharedPreference value + the timeout interval, also show the lock screen.
When the app is unlocked, set the appwide unlocked-since-boot state to true.
Besides the timeout, this approach will also automatically lock your app if your app is killed and restarts or if your phone restarts, but I don't think that's an especially bad problem for most apps. It's a little over-safe and may lock unecessarily on users who task switch a lot, but I think it's a worthwhile tradeoff for reduced code and complexity by a total removal of any background process / wakelock concerns (no services, alarms, or receivers necessary).
To work around process-killing locking the app regardless of time, instead of sharing an appwide singleton for unlocked-since-boot, you could use a SharedPreference and register a listener for the system boot broadcast intent to set that Preference to false. That re-adds some of the complexity of the initial solution with the benefit being a little more convenience in the case that the app's process is killed while backgrounded within the timeout interval, although for most apps it's probably overkill.
I dealt with this by using the AlarmManager to schedule and cancel timeout action.
Then in the onPause() event of all of my activites, I schedule the alarm. In the onResume() event of all of my activities, I check to see if the alarm goes off. If the alarm went off, I shutdown my app. If the alarm hasn't gone off yet I cancel it.
I created Timeout.java to manage my alarms. When the alarm goes off a intent is fired:
public class Timeout {
private static final int REQUEST_ID = 0;
private static final long DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes
private static PendingIntent buildIntent(Context ctx) {
Intent intent = new Intent(Intents.TIMEOUT);
PendingIntent sender = PendingIntent.getBroadcast(ctx, REQUEST_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return sender;
}
public static void start(Context ctx) {
ctx.startService(new Intent(ctx, TimeoutService.class));
long triggerTime = System.currentTimeMillis() + DEFAULT_TIMEOUT;
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC, triggerTime, buildIntent(ctx));
}
public static void cancel(Context ctx) {
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.cancel(buildIntent(ctx));
ctx.startService(new Intent(ctx, TimeoutService.class));
}
}
Then, I created a service to capture the intent generated by the alarm. It sets some global state in my instance of the application class to indicate that the app should lock:
public class TimeoutService extends Service {
private BroadcastReceiver mIntentReceiver;
#Override
public void onCreate() {
super.onCreate();
mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ( action.equals(Intents.TIMEOUT) ) {
timeout(context);
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(Intents.TIMEOUT);
registerReceiver(mIntentReceiver, filter);
}
private void timeout(Context context) {
App.setShutdown();
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancelAll();
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mIntentReceiver);
}
public class TimeoutBinder extends Binder {
public TimeoutService getService() {
return TimeoutService.this;
}
}
private final IBinder mBinder = new TimeoutBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
Finally, I created a subclass of Activity that all of my app's activities subclass from to manage locking and unlocking:
public class LockingActivity extends Activity {
#Override
protected void onPause() {
super.onPause();
Timeout.start(this);
}
#Override
protected void onResume() {
super.onResume();
Timeout.cancel(this);
checkShutdown();
}
private void checkShutdown() {
if ( App.isShutdown() ) {
finish();
}
}
}
Using onPause and onResume to start and stop the timeout gives me the following semantics. As long as one of my application's activities is active, the timeout clock is not running. Since I used an Alarm type of AlarmManager.RTC, whenever the phone goes to sleep the timeout clock runs. If the timeout happens while the phone is asleep, then my service will pick up the timeout as soon as the phone wakes up. Additionally, the clock runs when any other activity is open.
For a more detailed version of these, you can see how I actually implemented them in my application https://github.com/bpellin/keepassdroid
Check out how OpenIntents Safe implements this functionality.
This has been a really helpful post for me. To back the concept given by #Yoni Samlan . I have implemented it this way
public void pause() {
// Record timeout time in case timeout service is killed
long time = System.currentTimeMillis();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor edit = preferences.edit();
edit.putLong("Timeout_key", time);// start recording the current time as soon as app is asleep
edit.apply();
}
public void resume() {
// Check whether the timeout has expired
long cur_time = System.currentTimeMillis();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
long timeout_start = preferences.getLong("Timeout_key", -1);
// The timeout never started
if (timeout_start == -1) {
return;
}
long timeout;
try {
//timeout = Long.parseLong(sTimeout);
timeout=idle_delay;
} catch (NumberFormatException e) {
timeout = 60000;
}
// We are set to never timeout
if (timeout == -1) {
return;
}
if (idle){
long diff = cur_time - timeout_start;
if (diff >= timeout) {
//Toast.makeText(act, "We have timed out", Toast.LENGTH_LONG).show();
showLockDialog();
}
}
}
Call pause method from onPause and resume method from onResume.