From 9e0de9ac69102519bda1bdefe757897af122e21a Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 18 Apr 2018 22:34:13 +0200 Subject: [PATCH] rudimentary support for JobScheduler to run updates The new JobScheduler API can opportunitistically run a job based on whether there is good internet, connected to power, etc. This is very useful for running updates. Ideally, updates would always happen in the background while on unmetered internet and connected to power. #588 --- app/src/main/AndroidManifest.xml | 4 ++ .../org/fdroid/fdroid/UpdateJobService.java | 43 +++++++++++++++++ .../java/org/fdroid/fdroid/UpdateService.java | 47 +++++++++++++------ 3 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/org/fdroid/fdroid/UpdateJobService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2e9d78a04..7d2503634 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -258,6 +258,10 @@ + diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateJobService.java b/app/src/main/java/org/fdroid/fdroid/UpdateJobService.java new file mode 100644 index 000000000..73561ce19 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/UpdateJobService.java @@ -0,0 +1,43 @@ +package org.fdroid.fdroid; + +import android.annotation.TargetApi; +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.Intent; + +/** + * Interface between the new {@link android.app.job.JobScheduler} API and + * our old {@link UpdateService}, which is based on {@link android.app.IntentService}. + * This does not do things the way it should, e.g. stopping the job on + * {@link #onStopJob(JobParameters)} and properly reporting + * {@link #jobFinished(JobParameters, boolean)}, but this at least provides + * the nice early triggering when there is good power/wifi available. + * + * @see Project Volta: Scheduling jobs + */ +@TargetApi(21) +public class UpdateJobService extends JobService { + @Override + public boolean onStartJob(final JobParameters params) { + new Thread() { + @Override + public void run() { + // faking the actually run time + try { + startService(new Intent(UpdateJobService.this, UpdateService.class)); + Thread.sleep(2000); + } catch (InterruptedException e) { + // ignored + } finally { + jobFinished(params, false); + } + } + }.start(); + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + return false; + } +} diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index b30cbc13d..73f7591cd 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -22,7 +22,10 @@ import android.app.AlarmManager; import android.app.IntentService; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -118,27 +121,41 @@ public class UpdateService extends IntentService { } /** - * Schedule or cancel this service to update the app index, according to the - * current preferences. Should be called a) at boot, b) if the preference - * is changed, or c) on startup, in case we get upgraded. + * Schedule this service to update the app index while canceling any previously + * scheduled updates, according to the current preferences. Should be called + * a) at boot, b) if the preference is changed, or c) on startup, in case we get + * upgraded. It works differently on {@code android-21} and newer, versus older, + * due to the {@link JobScheduler} API handling it very nicely for us. + * + * @see Project Volta: Scheduling jobs */ - public static void schedule(Context ctx) { + public static void schedule(Context context) { int interval = Preferences.get().getUpdateInterval(); - Intent intent = new Intent(ctx, UpdateService.class); - PendingIntent pending = PendingIntent.getService(ctx, 0, intent, 0); + if (Build.VERSION.SDK_INT < 21) { + Intent intent = new Intent(context, UpdateService.class); + PendingIntent pending = PendingIntent.getService(context, 0, intent, 0); - AlarmManager alarm = (AlarmManager) ctx - .getSystemService(Context.ALARM_SERVICE); - alarm.cancel(pending); - if (interval > 0) { - alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + 5000, interval, pending); - Utils.debugLog(TAG, "Update scheduler alarm set"); + AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarm.cancel(pending); + if (interval > 0) { + alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, + SystemClock.elapsedRealtime() + 5000, interval, pending); + Utils.debugLog(TAG, "Update scheduler alarm set"); + } else { + Utils.debugLog(TAG, "Update scheduler alarm not set"); + } } else { - Utils.debugLog(TAG, "Update scheduler alarm not set"); + Utils.debugLog(TAG, "Using android-21 JobScheduler for updates"); + JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + jobScheduler.cancelAll(); + ComponentName componentName = new ComponentName(context, UpdateJobService.class); + JobInfo task = new JobInfo.Builder(0xfedcba, componentName) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) + .setOverrideDeadline(interval) + .build(); + jobScheduler.schedule(task); } - } /**