shelll shelll - 5 months ago 143
Android Question

Android exact Alarm is always 3 minutes off

I have an app which uses the

AlarmManager
to regularly wake up the phone at full hour and send a message to an Android Wear watch which than makes a short vibration. I have two users with a Samsung Galaxy S6 with stock Android 5.1.1 and the Sony SW 3 with 5.1.1 who experience a weird bug. At the very first full hour the vibration is at the exact time but all other vibrations are 3 minutes delayed. Sometimes even the first full hour vibration is delayed.

Here is some code:

final Calendar time = Calendar.getInstance();
time.set(Calendar.SECOND, 0);
time.set(Calendar.MILLISECOND, 0);
time.set(Calendar.MINUTE, 0);
time.set(Calendar.HOUR_OF_DAY, time.get(Calendar.HOUR_OF_DAY) + 1);

final Intent hourlyChimeIntent = new Intent(context, HourlyChimeReceiver.class);
hourlyChimeIntent.setAction(key);
final AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
final PendingIntent pi = PendingIntent.getBroadcast(context, 0, hourlyChimeIntent, PendingIntent.FLAG_CANCEL_CURRENT);
am.setExact(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(), pi);


I acquire a
WakeLock
in the receiver and then send a message to the Wear watch in a thread. No vibration is missed, they are just 3 minutes late.

I have no other reports about this issue and all my testing devices are working good. I have no Samsung device though.

Any ideas what could cause the 3 minutes delay? Does Samsung ignore
setExact
and makes my alarm an inexact? How to force exact alarms on Samsungs?

EDIT:

Here is the Android Wear specific code. In the receiver's
onReceive
method I do this:

final PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
final PowerManager.WakeLock lock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID);
lock.acquire(7L * 1000L);

final GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context).addApi(Wearable.API).build();

new Thread(new Runnable() {
@Override
public void run() {
googleApiClient.blockingConnect();

long pattern[];
pattern = new long[] {0L, 500L};

final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(2000L, TimeUnit.MILLISECONDS);

if (nodes != null) {
for (final Node node : nodes.getNodes()) {
// just send and forget
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), "/hourly_chime", Utils.Vibrator.serializeVibratePattern(pattern).getBytes()).await();
}
}
}
}).start();

Answer

The issue seems to occur only on Samsung devices (e.g. Galaxy Grand, S4, S5, S6, Note 3, Note 4) with Lollipop (5.0, 5.1, 5.1.1). It seems that alarms are scheduled inexact when device is on battery with screen off. If device is charging or has screen on during scheduling alarm the issue will not occur.

You can verify if next alarm will be inexact with:

adb shell dumpsys alarm

I didn't find perfect solution for this problem - only workarounds where each has some drawbacks:

  1. Use setAlarmClock instead setExact (see this answer). This works very well (not on all devices), but the issue with this solution is that the alarm will affect the system by showing alarm icon in status bar (if someone doesn't have alarm clock set already) and displaying next alarm scheduled time on alarm widgets etc. Unfortunately while this works on Galaxy Grand with 5.1.1 it doesn't on Galaxy S4 with 5.0.1.
  2. Enable screen before scheduling the alarm (I do this half second before scheduling next alarm to avoid race condition). Of course it is not good solution for every app to enable screen just to schedule next alarm.
  3. One bug report describing similar issue connects it with app package name length! I didn't verify if it really fixes the issue, because changing package name is not an option for already published app.
  4. There is another report where someone claims this can be fixed by using WakefulBroadcastReceiver, but it doesn't work in my case.

BTW This issue drives me crazy :)

Edit: Looks like this issue does not occur when there is keyword "alarm" or "alert" in the app package name (as pointed out by Mathieu H. in comments below).

Comments