Recommand · October 16, 2021 0

setExactAndAllowWhileIdle is triggered 60-90 minutes later on Samsung device with Android 11

I have read dozens of threads and tried multiple approach to make setExactAndAllowWhileIdle working well on S21 with Android 11. However the best I ended was setExactAndAllowWhileIdle fires alarm 60 minutes later then scheduled. Next attempt was 90 minutes delayed. I read that Samsung has modified AlarmManager handling, so it is more agresive, but such huge delays are almost not acceptable.

The issue is not seen at Samsung tablet with Android 8.1 Oreo, where alarm fires event almost exactly on time. Also Google Google Nexus 5X with Oreo fires Alarm Exactly on time

This is the code I ended up:

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
public void scheduledTrigger(String _desired_mode, int _desired_hour, int _desired_minute){

        //Create alarm manager
        AlarmManager alarmMgr = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);

        //Create pending intent & register it to your alarm notifier class
        Intent intent = new Intent(context, BroadcastHelper.class);
        intent.addFlags( Intent.FLAG_RECEIVER_FOREGROUND );
        intent.putExtra("_desired_mode", _desired_mode);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
        alarmMgr.cancel(pendingIntent);

        //null is emited by CANCEL button in dialog, so just cancel cancel pendingIntent and do early return
        if (_desired_mode == null) return;

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeZone( TimeZone.getDefault());
        calendar.set(Calendar.HOUR_OF_DAY, _desired_hour);
        calendar.set(Calendar.MINUTE, _desired_minute);
        calendar.set(Calendar.SECOND, 00);

        //these 4 lines ensuring alarm is not fired in the past
        long diff = Calendar.getInstance().getTimeInMillis() - calendar.getTimeInMillis();
        if (diff > 0) {
            calendar.add(Calendar.DATE, 1);
        }

        final int SDK_INT = Build.VERSION.SDK_INT;
        if (SDK_INT < Build.VERSION_CODES.KITKAT) {
            alarmMgr.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        }
        else if (Build.VERSION_CODES.KITKAT <= SDK_INT  && SDK_INT < Build.VERSION_CODES.M) {
            alarmMgr.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        }
        //setAlarmClock is needed to wake device in DOZE mode
        else if (SDK_INT >= Build.VERSION_CODES.M) {
            alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        }
    }
public class BroadcastHelper extends BroadcastReceiver {

    Helpers helpers;
    EnvironmentFragment environmentFragment;


    @Override
    public void onReceive(Context context, Intent intent) {
        System.out.println("BroadcastHelper triggered");
        String desired_mode = intent.getExtras().getString("desired_mode");

        PowerManager pm = (PowerManager) context
                .getSystemService(Context.POWER_SERVICE);
        final PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, desired_mode);
        wakeLock.acquire();
        helpers = new Helpers( context );

        try{
            System.out.println("Scheduled desired_mode:" + desired_mode);
            if (desired_mode != null) {
                environmentFragment = new EnvironmentFragment();
                environmentFragment.new setMode( context, null, desired_mode ).executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR );
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        wakeLock.release();
    }
}

Are there any specific for Samsung or Android 11, that I need to apply in order to get AlarmManager working on time or almost on time?