Android-job: Exact scheduling of Jobs doesn't work when App is killed for long time (>1 min) on One Plus 5T(Nougat)

Created on 18 Jan 2018  路  7Comments  路  Source: evernote/android-job

I am trying to show a notification once a day at exact time. The time is coming from backend API. Below code is working fine when app is in foreground or background. But when app is killed for long time (~1min) or after reboot I am not getting notification on One Plus 5T(Nougat) and also on Oppo device(Mashmallow). Also, sometimes when I relaunch the app, I get all the failed notifications at once. Everything's working fine on Asus Zenfone Max (Mashmallow). Alarms are triggering in every scenario in Asus (foreground, background, screen locked or even reboot)

Below is adb log for One Plus 5T

//when app is killed and before alarm scheduled time 
Shikhars-MacBook-Pro:HOGAndroid_New shikhardeep$ adb shell dumpsys alarm | grep "com.tf.eros.faythTv"
    ELAPSED_WAKEUP #0: Alarm{3a8c0ae type 2 when 1118303576 com.tf.eros.faythTv}
      tag=*walarm*:com.tf.eros.faythTv/com.evernote.android.job.v14.PlatformAlarmReceiver
      operation=PendingIntent{e233e4f: PendingIntentRecord{217c9dc com.tf.eros.faythTv broadcastIntent}}
    ELAPSED_WAKEUP #0: Alarm{44439ba type 2 when 1118303589 com.tf.eros.faythTv}
      tag=*walarm*:com.tf.eros.faythTv/com.evernote.android.job.v14.PlatformAlarmReceiver
      operation=PendingIntent{a7f706b: PendingIntentRecord{a45a7c8 com.tf.eros.faythTv broadcastIntent}}
  u0a355:com.tf.eros.faythTv +1s718ms running, 4 wakeups:
      *walarm*:com.tf.eros.faythTv/com.evernote.android.job.v14.PlatformAlarmReceiver

//when app is killed and just after the alarm scheduled time
Shikhars-MacBook-Pro:HOGAndroid_New shikhardeep$ adb shell dumpsys alarm | grep "com.tf.eros.faythTv"
  u0a355:com.tf.eros.faythTv +1s722ms running, 6 wakeups:
      *walarm*:com.tf.eros.faythTv/com.evernote.android.job.v14.PlatformAlarmReceiver

Code

public class ShowHoroscopeNotificationJob extends Job {

    public static final String TAG = "HOROSCOPE_NOTIFICATION";

    public ShowHoroscopeNotificationJob() {
        super();
    }

    @Override
    protected void onReschedule(int newJobId) {
        super.onReschedule(newJobId);
    }

    @NonNull
    @Override
    protected Result onRunJob(@NonNull Params params) {
        try {

            NotificationCompat.Builder mBuilder =
                    new NotificationCompat.Builder(getContext(), "HOG_NOTIFICATION")
                            .setSmallIcon(R.drawable.notification_icon)
                            .setLargeIcon(BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.ic_launcher))
                            .setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary))
                            .setContentTitle("Horoscope Reminder")
                            .setContentText("Check out Daily Horoscope !!!")
                            .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                            .setAutoCancel(true);

            NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);

            if (notificationManager != null) {
                notificationManager.notify(new Random().nextInt(40) + 1, mBuilder.build()); 
                schedule(getExactTime(ZPreferences.getHoroscopeReminderTime(getContext()))); //again calling schedule() as setExact() can't be periodic 
                return Result.SUCCESS;
           }

        } catch (Exception e) {

            e.printStackTrace();
            return Result.RESCHEDULE;
        }
    }

    public static void schedule(long exactInMs) {

        if (exactInMs != 0L) {

            new JobRequest.Builder(ShowHoroscopeNotificationJob.TAG)
                    .setExact(exactInMs)
                    .build()
                    .schedule();
        }

    }

question

All 7 comments

https://github.com/evernote/android-job/wiki/FAQ#what-happens-with-jobs-after-the-app-was-forced-killed

Oh, so even if I use AlarmManager I can't schedule notifications for exact time? But then, how 3rd party alarm apps can trigger alarms at the same time even if the difference between 2 alarms is only 1 minute. Do they apply for whitelisting or they use some other techniques?

Some manufacturers may implement different strategies. Also, Android is blocking your alarms if you use them too frequently, see https://developer.android.com/reference/android/app/AlarmManager.html#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent)

thanks for the quick response. I will see whether AlarmManager APIs will work on not. I was going through some StackOverflow answers and it seems even setExactAndAllowWhileIdle() doesn't guarantee exact time. https://stackoverflow.com/questions/33110246/setexactandallowwhileidle-is-not-exact-as-of-developer-reference

So, I tried with standalone AlarmManager APIs and still the same behaviour. Even worse, I was not able to receive BOOT_COMPLETED broadcast on One Plus and Oppo. I did more research and it seems that this is happening because of custom battery optimisation imposed by manufacturer.
On One plus 5T-> settings->batter optimisation->select the app and choose don't optimise

adb shell dumpsys deviceidle whitelist
system-excidle,cn.oneplus.photos,10084
system-excidle,com.android.providers.downloads,10011
system-excidle,com.android.vending,10047
system-excidle,com.google.android.gms,10014
system,cn.oneplus.photos,10084
system,com.android.providers.downloads,10011
system,com.google.android.gms,10014
user,com.truecaller,10234 //Don't know how truecaller is the only app under user in whitelist
user,com.tf.eros.faythTv,10355 // My App

& on Oppo -> Security center app ->privacy permission->startup manager->select the app and choose allow. In Oppo adb shell dumpsys deviceidle whitelist throwsCan't find service: whitelist.

After this I am able to create notification when app is killed or after restart on both devices and I am also receiving BOOT_COMPLETED broadcast
Sample log statement:
01-19 18:09:12.804 com.tf.eros.faythTv D/HOG_ALARM_MANAGER: OUTSIDE_IF()---got BOOT COMPLETED BROADCAST

This is seriously frustrating :-( . My app users are mostly old people and I can't guide them to go to battery manager and turn on this settings

UPDATE:
https://stackoverflow.com/questions/41524459/broadcast-receiver-not-working-after-device-reboot-in-android
the accepted solution talks about something called as 'AccessibilityService' in order to receive BOOT_COMPLETED broadcast. but I think its a hack to bypass these battery optimisation and not the right approah.

UPDATE:
The library also works fine as expected if the app is whitelisted in these battery manager Apps. I am going with this library as it takes out the pain of rescheduling jobs after reboot, creating a receiver for BOOT_COMPLETED and a lot of boilerplate code

Thanks for the update, that info will be helpful in the future.

I just read this article about AlarmManager and they said the solution to this problem was to put the android:process=":remote" attribute in the <receiver> element for the BroadcastReceiver class setup for use by the AlarmManager. However, in this library, we use Jobs, so we can't just edit the manifest for the BroadcastReceiver like this. Is there any equivalent to android:process=":remote" in the Evernote library? If you haven't already done it, is there anyway to integrate this attribute into your library?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ExploiTR picture ExploiTR  路  6Comments

adek picture adek  路  6Comments

tatocaster picture tatocaster  路  6Comments

krokyze picture krokyze  路  3Comments

karntrehan picture karntrehan  路  5Comments