1

This is my first question on stackoverflow. I have read many similar topics but have been unable to find the problem and I am getting a bit desperate.

I develop an app with an alarm clock. This alarm clock only rings under certain circumstances. If they don't apply, I send a notification to the user and set the next alarm 10 minutes later using setAlarmClock(). If the conditions don't apply until a user specified time, the alarm never goes off.

Now when I leave the phone unobserved for a while and check the notifications, I see that they didn't come in every 10 minutes, but very irregularly. Sometimes it came after 10 minutes, other times 11, 15, 30, or whatever.

The problem only occurs when the phone is not plugged in, and so I figured it has something to do with Android's doze mode. setAlarmClock() should help with this problem but it doesn't. I also tried setExactAndAllowWhileIdle(), but that didn't work either. So if anyone has any idea what the issue might be, I would be really greatful to hear about it.

This is my code: First, the two methods inside my AlarmController

//this is called the first time an alarm is set
public void registerAlarm(int id, AlarmClock alarm) {
        if (alarmManager == null)
            alarmManager = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);

        Calendar alarmTime = alarm.getAlarmCalendar();

        //create alarm intent
        Intent intent = new Intent("START_ALARM");
        intent.putExtra("alarm_id",id);
        PendingIntent alarmIntent = PendingIntent.getBroadcast(ctx, id, intent, 0);

        AlarmManager.AlarmClockInfo info = new AlarmManager.AlarmClockInfo(alarmTime.getTimeInMillis(),alarmIntent);
        alarmManager.setAlarmClock(info, alarmIntent);
        //alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,alarmTime.getTimeInMillis(),alarmIntent);
    }

// This is called if the conditions didn't apply
public void registerSnoozeAlarm(int id, int minutes) {
        if (alarmManager == null)
            alarmManager = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);

        long alarmTime = System.currentTimeMillis() + 1000*60*minutes;

        //create alarm intent
        Intent intent = new Intent("START_ALARM");
        intent.putExtra("alarm_id",id);
        PendingIntent alarmIntent = PendingIntent.getBroadcast(ctx, id, intent, 0);

        AlarmManager.AlarmClockInfo info = new AlarmManager.AlarmClockInfo(alarmTime, alarmIntent);
        alarmManager.setAlarmClock(info, alarmIntent);
        //alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime,alarmIntent);
    }

The Receiver in the manifest:

<receiver android:name="packagename.AlarmReceiver"
          android:exported="false">
          <intent-filter>
              <action android:name="START_ALARM" >
              </action>
          </intent-filter>
</receiver>

This is my AlarmReceiver:

public class AlarmReceiver extends WakefulBroadcastReceiver implements Observer{
    private Context context;
    private AlarmClock alarm;
    private Intent intent;

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;
        this.intent = intent;

        //request data from server
        Bundle extras = intent.getExtras();
        int alarmID = extras.getInt("alarm_id");
        alarm = DataHandler.getInstance(context).getAlarm(alarmID);
        DataHandler.getInstance(context).requestDataForAlarm(alarm,this);
    }

    @Override
    public void update(Observable observable, Object data) {
        Measurement measurement = (Measurement) data;

        if (*someConditionapplies*){
            //play ringtone
            AlarmSoundController sc = AlarmSoundController.getInstance(context);
            sc.playSound(alarm.getAlarmSoundPath());

            //send a notification message
            ComponentName comp = new ComponentName(context.getPackageName(), AlarmNotificationService.class.getName());
            intent.putExtra("alarm_id",alarm.getId());
            intent.putExtra("description", alarm.getDescription());
            intent.putExtra("data", measurement.getData());
            startWakefulService(context, (intent.setComponent(comp)));
            setResultCode(Activity.RESULT_OK);
        }
        else {
            //if user specified end time is reached, send final notification, else try again in 10 minutes

            Calendar alarmEndTime = alarm.getAlarmEndCalendar();
            Calendar now = Calendar.getInstance();

            if (now.before(alarmEndTime)) {
                //schedule new alarm in 10 minutes
                DataHandler.getInstance(context).registerSnoozeAlarm(alarm.getId(),10);

                sendNoAlarmNotification(measurement, false);
                setResultCode(Activity.RESULT_OK);
            }
            else{
                //send the last notification message
                sendNoAlarmNotification(measurement, true);
                setResultCode(Activity.RESULT_OK);
            }
        }
        DataHandler.getInstance(context).removeObserver(this);
    }

    private void sendNoAlarmNotification(Measurement measurement, boolean lastNotification) {
        ComponentName comp = new ComponentName(context.getPackageName(), NoAlarmNotificationService.class.getName());
        intent.putExtra("alarm_id", alarm.getId());
        intent.putExtra("description", alarm.getDescription());
        intent.putExtra("wind", measurement.windAvg);
        intent.putExtra("last_notification", lastNotification);
        intent.setComponent(comp);
        startWakefulService(context, intent);
    }
}

Thanks a lot in advance!

Lo-Cool
  • 11
  • 2
  • You have an awful lot of code in `AlarmReceiver` -- a `WakefulBroadcastReceiver` should not be doing very much work. It is unclear if you are assuming that some singleton `DataHandler` will always be around (it won't, because your process may terminate) or if you are doing some sort of disk or network I/O (not a good plan, since you are on the main application thread). I recommend that you start by commenting all of that out, and see if `AlarmReceiver` gets control at the desired times. Once you have that stabilized, then start slowly reintroducing this logic. – CommonsWare Mar 18 '17 at 11:39
  • Thank you for the reply. As you might tell, I'm not overly experienced with Android but I am keen to learn. I will give your suggestion a try. Would you suggest to execute the code in the IntentService started by `startWakefulService`? – Lo-Cool Mar 18 '17 at 14:29
  • Work that will take over 1ms should be delegated to the `IntentService`, as `onReceive()` is called on the main application thread. If you happen to have UI in the foreground at the time `onReceive()` is called, your UI will be frozen while `onReceive()` does its work. The background thread that you get from the `IntentService` helps here. – CommonsWare Mar 18 '17 at 14:33
  • Thanks a lot! That appears to have caused the problem. – Lo-Cool Mar 27 '17 at 09:55

0 Answers0