Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alarm Manager Reliability

I've been struggling with this problem for days. I've also checked the documentation and several topics but didn't find any solution / explanation.
I am testing my application on LG p500 but I did a few test on Droid too and I get the same result.

My application uses AlarmHandler to schedule alarm. The application works correctly on the emulator and also on the device until the device has enough free memory. When I start several other applications on the device and the memory is low the alarm will not fire anymore. As soon as I stop the "other" application the alarm works fine again.

Let me report the test and the result.

  1. I set an alarm on my application 10 minute later.
  2. I start several application (browser, google map, gmail, K9Mail,....)
  3. I start the catlog to see the log of my application
  4. Wait 15 minute without working on the phone
  5. After 10 minutes the alarm should be fired but nothing happen until I wakeup my phone pressing a button
  6. When I wake-up my phone the alarm immediatly fires and all the notificatin happen.
  7. I stop the "other" application I previously started (browser, google map,...)
  8. Set again an alarm 10 minute later
  9. I start the catlog to see the log of my application
  10. Wait without working on the phone
  11. 10 minutes later the alarm fires and I get notified.

I did this test several time and I get the same result.
Then I tried to set an alarm using the "Catch" application I previously downloaded from the market and I get the same behaviour so it looks like this is not a problem of my application.

Looking at the log of my application I do not see any error / exception but it looks like that when the system is low on memory something happen and the broadcast receiver does not start until the phone is waked up throught the keyboard. As soon as I wake-up the phone the receiver start and all the notification happen.

Here the code I used:

The Receiver:

public class NotificationReceiver extends BroadcastReceiver
{
public static final String LOG_TAG = "YAAS - Notification Receiver";

@Override
public void onReceive(Context context, Intent intent)
{
    ScheduleActivityService.acquireStaticLock(context);
    Log.i(LOG_TAG, "Received alarm - id: " + intent.getIntExtra("id", -1));
    Intent intent2 = new Intent(context, ScheduleActivityService.class);
    intent2.putExtra("id", intent.getIntExtra("id", -1));
    context.startService(intent2);
}
}

The Service

public class ScheduleActivityService extends Service
{
public static final String LOCK_NAME_STATIC="it.hp.yaas.AppService.Static";
public static final String LOG_TAG = "YAAS - ActivityService";
private static PowerManager.WakeLock lockStatic = null;

private final IBinder mBinder = new LocalBinder();

public class LocalBinder extends Binder
{
    public ScheduleActivityService getService()
    {
        return ScheduleActivityService.this;
    }
}

@Override
public IBinder onBind(Intent intent)
{
    return mBinder;
}

public static void acquireStaticLock(Context context) {
    getLock(context).acquire();
}

synchronized private static PowerManager.WakeLock getLock(Context context)
{
    if (lockStatic == null)
    {
        PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOCK_NAME_STATIC);
        lockStatic.setReferenceCounted(true);
    }
    return(lockStatic);
}

/**
 * This method is called when an alarm fires that is its alarm time is reached.
 * The system assume that the alarm fired match the alarm time of the first
 * activity.
 * @param intent intent fired
 * @param flag
 * @param startId
 */
@Override
public int onStartCommand(Intent intent, int flag, int startId)
{
    super.onStartCommand(intent, flag, startId);
    try {
        Log.i(LOG_TAG, "Alarm fired: " + startId + " - id: " + intent.getIntExtra("id", -1));
        AlarmHandler.getInstance().onAlarmFired(intent.getIntExtra("id", -1));
    }
    finally { getLock(this).release(); }
    return START_STICKY;
}

@Override
public void onDestroy()
{
    super.onDestroy();
    Log.i(LOG_TAG,  "Destroy");
}
}

An piece of code from AlarmHandler, the routine called to schedule the alarm:

    public synchronized void onAlarmFired(int alarmId)
{
    scheduledAlarmId = -1;
    Alarm alarmFired = pop();
    if (alarmFired == null) return;
    Log.i(LOG_TAG, "onAlarmFired (Alarm: " + alarmFired + ") at (time: " + Utilities.convertDate(new Date(), "HH:mm:ss") + ")");
    notifyAlarmListener(alarmFired);
    if (alarmFired.reschedule(null) != null) add(alarmFired);
    Alarm alarm = peek();
    if (alarm != null && scheduledAlarmId != alarm.getId()) scheduleEvent(alarm);
}

/**
 * Schedule an alarm through AlarmManager that trigger next activity notification
 * @param alarm alarm to be scheduled
 */
private void scheduleEvent(Alarm alarm)
{
    Log.i(LOG_TAG, "scheduleEvent - (Alarm: " + alarm + ")");
    Intent intent = new Intent(context, NotificationReceiver.class);
    intent.putExtra("id", alarm.getId());
    // In reality, you would want to have a static variable for the request code instead of 192837
    PendingIntent sender = PendingIntent.getBroadcast(context, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    // Get the AlarmManager service
    AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    am.set(AlarmManager.RTC_WAKEUP, alarm.getTime().getTime(), sender);
    scheduledAlarmId = alarm.getId();
}

And finally this is a piece of Manifest file:

    <activity android:name=".ListActivity"
          android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>

    <activity android:name=".EditActivity"/>

    <activity android:name=".SettingsActivity"/>

    <service android:name="ScheduleActivityService"
             android:label="YAAS Service"/>

    <receiver  android:name="NotificationReceiver" />
like image 244
Marco Cravero Avatar asked Aug 08 '11 15:08

Marco Cravero


People also ask

Does alarm Manager persist even after reboot?

The AlarmManager service is a convenient class to schedule working on your Android application; however, when the device shuts down or reboots, all your alarms will be lost since the system does not retain them between system restarts.

What is the use of alarm manager?

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.

How does an application get access to the alarm manager?

How does an application get access to the AlarmManager? Use the AlarmManager() constructor to create an instance of the AlarmManager. Use the AlarmManager. newInstance() method to retrieve the singleton instance of the AlarmManager.

How do I cancel alarm Manager?

In order to delete : AlarmManager alarmManager = (AlarmManager) getSystemService(Context. ALARM_SERVICE); Intent myIntent = new Intent(getApplicationContext(), SessionReceiver.


1 Answers

Are you sure your process doesn't get killed when you start all those applications? If it does, the alarms you set will die with it. It's not exactly clear who and when schedules the alarm in your code, but if it's the service, since it's sticky, it will eventually gets re-started, and you will get an alarm at some point (when you wake the device).

An easy way to check what alarms are registered at different points of your testing:

# adb shell dumpsys alarm
like image 64
Nikolay Elenkov Avatar answered Sep 27 '22 17:09

Nikolay Elenkov