Improve the automatic updates checks

The builds for each device are released more or less at the same
time of the day, so check when the last build was released and
schedule the next check accordingly. Also, allow multiple automatic
updates check in the same day. There's no need to limit the number
of checks since they are not frequent and this can affect negatively
the effectiveness of the new algorithm. In addition to that, remove
any pending oneshot alarm in case a subsequent check succeeds and
log every new alarm.

Change-Id: I4668f2e342e51d3578992eec6d8c270065d9aa21
This commit is contained in:
Gabriele M
2017-08-31 17:29:50 +02:00
parent 5c09d5c5f1
commit 6f3dfda1b0
2 changed files with 115 additions and 44 deletions

View File

@@ -327,6 +327,12 @@ public class UpdatesActivity extends UpdatesListActivity {
long millis = System.currentTimeMillis();
preferences.edit().putLong(Constants.PREF_LAST_UPDATE_CHECK, millis).apply();
updateLastCheckedString();
if (json.exists() && preferences.getBoolean(Constants.PREF_AUTO_UPDATES_CHECK, true) &&
Utils.checkForNewUpdates(json, jsonNew)) {
UpdatesCheckReceiver.updateRepeatingUpdatesCheck(this);
}
// In case we set a one-shot check because of a previous failure
UpdatesCheckReceiver.cancelUpdatesCheck(this);
jsonNew.renameTo(json);
} catch (IOException | JSONException e) {
Log.e(TAG, "Could not read json", e);

View File

@@ -31,9 +31,13 @@ import org.json.JSONException;
import org.lineageos.updater.download.DownloadClient;
import org.lineageos.updater.misc.Constants;
import org.lineageos.updater.misc.Utils;
import org.lineageos.updater.model.UpdateInfo;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class UpdatesCheckReceiver extends BroadcastReceiver {
@@ -59,54 +63,54 @@ public class UpdatesCheckReceiver extends BroadcastReceiver {
scheduleRepeatingUpdatesCheck(context);
}
long lastCheck = preferences.getLong(Constants.PREF_LAST_UPDATE_CHECK, -1);
final long currentMillis = System.currentTimeMillis();
if (currentMillis > lastCheck + AlarmManager.INTERVAL_DAY) {
if (!Utils.isNetworkAvailable(context)) {
Log.d(TAG, "Network not available, scheduling new check");
if (!Utils.isNetworkAvailable(context)) {
Log.d(TAG, "Network not available, scheduling new check");
scheduleUpdatesCheck(context);
return;
}
final File json = Utils.getCachedUpdateList(context);
final File jsonNew = new File(json.getAbsolutePath() + ".tmp");
String url = Utils.getServerURL(context);
DownloadClient.DownloadCallback callback = new DownloadClient.DownloadCallback() {
@Override
public void onFailure(boolean cancelled) {
Log.e(TAG, "Could not download updates list, scheduling new check");
scheduleUpdatesCheck(context);
return;
}
final File json = Utils.getCachedUpdateList(context);
final File jsonNew = new File(json.getAbsolutePath() + ".tmp");
String url = Utils.getServerURL(context);
DownloadClient.DownloadCallback callback = new DownloadClient.DownloadCallback() {
@Override
public void onFailure(boolean cancelled) {
Log.e(TAG, "Could not download updates list, scheduling new check");
@Override
public void onResponse(int statusCode, String url,
DownloadClient.Headers headers) {
}
@Override
public void onSuccess(File destination) {
try {
if (json.exists() && Utils.checkForNewUpdates(json, jsonNew)) {
showNotification(context);
updateRepeatingUpdatesCheck(context);
}
jsonNew.renameTo(json);
long currentMillis = System.currentTimeMillis();
preferences.edit()
.putLong(Constants.PREF_LAST_UPDATE_CHECK, currentMillis)
.apply();
// In case we set a one-shot check because of a previous failure
cancelUpdatesCheck(context);
} catch (IOException | JSONException e) {
Log.e(TAG, "Could not parse list, scheduling new check", e);
scheduleUpdatesCheck(context);
}
}
};
@Override
public void onResponse(int statusCode, String url,
DownloadClient.Headers headers) {
}
@Override
public void onSuccess(File destination) {
try {
if (json.exists() && Utils.checkForNewUpdates(json, jsonNew)) {
showNotification(context);
}
jsonNew.renameTo(json);
preferences.edit()
.putLong(Constants.PREF_LAST_UPDATE_CHECK, currentMillis)
.apply();
} catch (IOException | JSONException e) {
Log.e(TAG, "Could not parse list, scheduling new check", e);
scheduleUpdatesCheck(context);
}
}
};
DownloadClient downloadClient = new DownloadClient.Builder()
.setUrl(url)
.setDestination(jsonNew)
.setDownloadCallback(callback)
.build();
downloadClient.start();
}
DownloadClient downloadClient = new DownloadClient.Builder()
.setUrl(url)
.setDestination(jsonNew)
.setDownloadCallback(callback)
.build();
downloadClient.start();
}
private static void showNotification(Context context) {
@@ -129,12 +133,21 @@ public class UpdatesCheckReceiver extends BroadcastReceiver {
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
public static void updateRepeatingUpdatesCheck(Context context) {
cancelRepeatingUpdatesCheck(context);
scheduleRepeatingUpdatesCheck(context);
}
public static void scheduleRepeatingUpdatesCheck(Context context) {
long millisToNextRelease = millisToNextRelease(context);
PendingIntent updateCheckIntent = getRepeatingUpdatesCheckIntent(context);
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_DAY,
SystemClock.elapsedRealtime() + millisToNextRelease,
AlarmManager.INTERVAL_DAY, updateCheckIntent);
Date nextCheckDate = new Date(System.currentTimeMillis() + millisToNextRelease);
Log.d(TAG, "Setting daily updates check: " + nextCheckDate);
}
public static void cancelRepeatingUpdatesCheck(Context context) {
@@ -149,15 +162,67 @@ public class UpdatesCheckReceiver extends BroadcastReceiver {
}
public static void scheduleUpdatesCheck(Context context) {
long millisToNextCheck = AlarmManager.INTERVAL_HOUR * 2;
PendingIntent updateCheckIntent = getUpdatesCheckIntent(context);
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HOUR * 2,
SystemClock.elapsedRealtime() + millisToNextCheck,
updateCheckIntent);
Date nextCheckDate = new Date(System.currentTimeMillis() + millisToNextCheck);
Log.d(TAG, "Setting one-shot updates check: " + nextCheckDate);
}
public static void cancelUpdatesCheck(Context context) {
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.cancel(getUpdatesCheckIntent(context));
Log.d(TAG, "Cancelling pending one-shot check");
}
private static long millisToNextRelease(Context context) {
final long extraMillis = 3 * AlarmManager.INTERVAL_HOUR;
List<UpdateInfo> updates = null;
try {
updates = Utils.parseJson(Utils.getCachedUpdateList(context), false);
} catch (IOException | JSONException ignored) {
}
if (updates == null || updates.size() == 0) {
return SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_DAY;
}
long buildTimestamp = 0;
for (UpdateInfo update : updates) {
if (update.getTimestamp() > buildTimestamp) {
buildTimestamp = update.getTimestamp();
}
}
buildTimestamp *= 1000;
Calendar c = Calendar.getInstance();
long now = c.getTimeInMillis();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
c.setTimeInMillis(c.getTimeInMillis() + millisSinceMidnight(buildTimestamp));
long millisToNextRelease = (c.getTimeInMillis() - now);
millisToNextRelease += extraMillis;
if (c.getTimeInMillis() < now) {
millisToNextRelease += AlarmManager.INTERVAL_DAY;
}
return millisToNextRelease;
}
private static long millisSinceMidnight(long millis) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(millis);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
return millis - c.getTimeInMillis();
}
}