Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions app/src/main/java/org/lineageos/updater/UpdateImporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import android.util.Log;

import org.lineageos.updater.controller.UpdaterController;
import org.lineageos.updater.misc.StringGenerator;
import org.lineageos.updater.misc.Utils;
import org.lineageos.updater.model.Update;
import org.lineageos.updater.model.UpdateStatus;
Expand All @@ -36,7 +35,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
Expand Down Expand Up @@ -143,8 +141,6 @@ private File importFile(Uri uri) throws IOException {

private Update buildLocalUpdate(File file) {
final long timeStamp = getTimeStamp(file);
final String buildDate = StringGenerator.getDateLocalizedUTC(
activity, DateFormat.MEDIUM, timeStamp);
final String name = activity.getString(R.string.local_update_name);
final Update update = new Update();
update.setAvailableOnline(false);
Expand All @@ -155,7 +151,7 @@ private Update buildLocalUpdate(File file) {
update.setTimestamp(timeStamp);
update.setStatus(UpdateStatus.VERIFIED);
update.setPersistentStatus(UpdateStatus.Persistent.VERIFIED);
update.setVersion(String.format("%s (%s)", name, buildDate));
update.setVersion(name);
return update;
}

Expand Down
25 changes: 16 additions & 9 deletions app/src/main/java/org/lineageos/updater/UpdaterReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import androidx.core.app.NotificationCompat;
import androidx.preference.PreferenceManager;

import org.lineageos.updater.controller.UpdaterService;
import org.lineageos.updater.misc.BuildInfoUtils;
import org.lineageos.updater.misc.Constants;
import org.lineageos.updater.misc.StringGenerator;
Expand All @@ -42,18 +43,15 @@ public class UpdaterReceiver extends BroadcastReceiver {
private static final String INSTALL_ERROR_NOTIFICATION_CHANNEL =
"install_error_notification_channel";

private static boolean shouldShowUpdateFailedNotification(Context context) {
private static boolean isUpdateSuccessful(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);

// We can't easily detect failed re-installations
if (preferences.getBoolean(Constants.PREF_INSTALL_AGAIN, false) ||
preferences.getBoolean(Constants.PREF_INSTALL_NOTIFIED, false)) {
return false;
}

long buildTimestamp = SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0);
long lastBuildTimestamp = preferences.getLong(Constants.PREF_INSTALL_OLD_TIMESTAMP, -1);
return buildTimestamp == lastBuildTimestamp;
// We can't easily detect failed re-installations.
boolean isReinstall = preferences.getBoolean(Constants.PREF_INSTALL_AGAIN, false);

return isReinstall || buildTimestamp != lastBuildTimestamp;
}

private static void showUpdateFailedNotification(Context context) {
Expand Down Expand Up @@ -91,9 +89,18 @@ public void onReceive(Context context, Intent intent) {
pm.reboot(null);
} else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
String downloadId = pref.getString(Constants.PREF_NEEDS_REBOOT_ID, null);
pref.edit().remove(Constants.PREF_NEEDS_REBOOT_ID).apply();

if (shouldShowUpdateFailedNotification(context)) {
if (downloadId != null && isUpdateSuccessful(context)) {
Intent cleanupIntent = new Intent(context, UpdaterService.class);
cleanupIntent.setAction(UpdaterService.ACTION_POST_REBOOT_CLEANUP);
cleanupIntent.putExtra(UpdaterService.EXTRA_DOWNLOAD_ID, downloadId);
context.startService(cleanupIntent);
}

if (!pref.getBoolean(Constants.PREF_INSTALL_NOTIFIED, false)
&& !isUpdateSuccessful(context)) {
pref.edit().putBoolean(Constants.PREF_INSTALL_NOTIFIED, true).apply();
showUpdateFailedNotification(context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public class UpdaterService extends Service {
public static final String ACTION_INSTALL_SUSPEND = "action_install_suspend";
public static final String ACTION_INSTALL_RESUME = "action_install_resume";

public static final String ACTION_POST_REBOOT_CLEANUP = "action_post_reboot_cleanup";

private static final String ONGOING_NOTIFICATION_CHANNEL =
"ongoing_notification_channel";

Expand Down Expand Up @@ -181,6 +183,10 @@ public int onStartCommand(Intent intent, int flags, int startId) {
mUpdaterController);
installer.reconnect();
}
} else if (ACTION_POST_REBOOT_CLEANUP.equals(intent.getAction())) {
String downloadId = intent.getStringExtra(EXTRA_DOWNLOAD_ID);
handlePostRebootCleanup(downloadId);
tryStopSelf();
} else if (ACTION_DOWNLOAD_CONTROL.equals(intent.getAction())) {
String downloadId = intent.getStringExtra(EXTRA_DOWNLOAD_ID);
int action = intent.getIntExtra(EXTRA_DOWNLOAD_CONTROL, -1);
Expand All @@ -194,6 +200,10 @@ public int onStartCommand(Intent intent, int flags, int startId) {
} else if (ACTION_INSTALL_UPDATE.equals(intent.getAction())) {
String downloadId = intent.getStringExtra(EXTRA_DOWNLOAD_ID);
UpdateInfo update = mUpdaterController.getUpdate(downloadId);
if (update == null) {
Log.e(TAG, "Update not found: " + downloadId);
return START_NOT_STICKY;
}
if (update.getPersistentStatus() != UpdateStatus.Persistent.VERIFIED) {
throw new IllegalArgumentException(update.getDownloadId() + " is not verified");
}
Expand Down Expand Up @@ -346,6 +356,7 @@ private void handleUpdateStatusChange(UpdateInfo update) {
}
case VERIFIED: {
stopForeground(STOP_FOREGROUND_DETACH);
mNotificationBuilder.mActions.clear();
mNotificationBuilder.setStyle(null);
mNotificationBuilder.setSmallIcon(R.drawable.ic_system_update);
mNotificationBuilder.setProgress(0, 0, false);
Expand All @@ -360,6 +371,7 @@ private void handleUpdateStatusChange(UpdateInfo update) {
}
case VERIFICATION_FAILED: {
stopForeground(STOP_FOREGROUND_DETACH);
mNotificationBuilder.mActions.clear();
mNotificationBuilder.setStyle(null);
mNotificationBuilder.setSmallIcon(android.R.drawable.stat_sys_warning);
mNotificationBuilder.setProgress(0, 0, false);
Expand Down Expand Up @@ -411,14 +423,6 @@ private void handleUpdateStatusChange(UpdateInfo update) {
mNotificationBuilder.setAutoCancel(true);
mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());

SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
boolean deleteUpdate = pref.getBoolean(Constants.PREF_AUTO_DELETE_UPDATES, false);
boolean isLocal = Update.LOCAL_ID.equals(update.getDownloadId());
// Always delete local updates
if (deleteUpdate || isLocal) {
mUpdaterController.deleteUpdate(update.getDownloadId());
}

tryStopSelf();
break;
}
Expand Down Expand Up @@ -540,4 +544,27 @@ private PendingIntent getResumeInstallationPendingIntent() {
return PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}

private void handlePostRebootCleanup(String downloadId) {
if (downloadId == null) {
return;
}

UpdateInfo update = mUpdaterController.getUpdate(downloadId);
if (update == null) {
Log.w(TAG, "Update not found during post-reboot cleanup: " + downloadId);
return;
}

Log.d(TAG, "Post-reboot cleanup for: " + downloadId);

SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
boolean deleteUpdate = pref.getBoolean(Constants.PREF_AUTO_DELETE_UPDATES, false);

// Always delete local updates
boolean isLocal = Update.LOCAL_ID.equals(downloadId);
if (deleteUpdate || isLocal) {
mUpdaterController.deleteUpdate(downloadId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public class HttpURLConnectionClient implements DownloadClient {

private final static String TAG = "HttpURLConnectionClient";

// Ref: mozilla-mobile/firefox-android AbstractFetchDownloadService.CHUNK_SIZE
private static final int CHUNK_SIZE = 32 * 1024;

private HttpURLConnection mClient;

private final File mDestination;
Expand Down Expand Up @@ -133,6 +136,7 @@ private class DownloadThread extends Thread {
private long mLastMillis = 0;
private long mSpeed = -1;
private long mEta = -1;
private double mLastEta = -1;

private final boolean mResume;

Expand All @@ -147,6 +151,7 @@ private void calculateSpeed(boolean justResumed) {
// ETA since the delta will grow, resulting in a very low speed
mLastMillis = millis;
mSpeed = -1; // we don't want the moving avg with values from who knows when
mLastEta = -1; // reset smoothed ETA so the first post-resume value is accepted as-is

// need to do this as well, otherwise the second time we call calculateSpeed(),
// the difference (mTotalBytesRead - mCurSampleBytes) will be larger than expected,
Expand All @@ -168,10 +173,31 @@ private void calculateSpeed(boolean justResumed) {
}
}

// Ref: mozilla-central DownloadsCommon.sys.mjs smoothSeconds()
private void calculateEta() {
if (mSpeed > 0) {
mEta = (mTotalBytes - mTotalBytesRead) / mSpeed;
if (mSpeed <= 0) return;

double rawSeconds = (double) (mTotalBytes - mTotalBytesRead) / mSpeed;

// Apply smoothing only when the new value is more than half the previous;
// large drops (e.g. after resume) are accepted immediately.
if (mLastEta >= 0 && rawSeconds > mLastEta / 2) {
double diff = rawSeconds - mLastEta;
// Asymmetric: trust 30% of a decrease, only 10% of an increase.
rawSeconds = mLastEta + (diff < 0 ? 0.3 : 0.1) * diff;

// If the change is tiny (< 5 s or < 5%), nudge by a small amount
// so the display shows forward progress rather than freezing.
diff = rawSeconds - mLastEta;
double diffPct = (diff / mLastEta) * 100;
if (Math.abs(diff) < 5 || Math.abs(diffPct) < 5) {
rawSeconds = mLastEta - (diff < 0 ? 0.4 : 0.2);
}
}

// Never show zero seconds while still downloading.
mLastEta = Math.max(rawSeconds, 1.0);
mEta = (long) mLastEta;
}

private void changeClientUrl(URL newUrl) throws IOException {
Expand Down Expand Up @@ -204,7 +230,7 @@ private DuplicateLink(String url, int priority) {

// https://tools.ietf.org/html/rfc6249
// https://tools.ietf.org/html/rfc5988#section-5
String regex = "(?i)<(.+)>\\s*;\\s*rel=duplicate(?:.*pri=([0-9]+).*|.*)?";
String regex = "(?i)<([^>]+)>\\s*;\\s*rel=duplicate(?:.*pri=([0-9]+).*|.*)?";
Pattern pattern = Pattern.compile(regex);
for (String field : entry.getValue()) {
Matcher matcher = pattern.matcher(field);
Expand Down Expand Up @@ -283,7 +309,7 @@ public void run() {
OutputStream outputStream = new FileOutputStream(mDestination, mResume)
) {
mTotalBytes = mClient.getContentLengthLong() + mTotalBytesRead;
byte[] b = new byte[8192];
byte[] b = new byte[CHUNK_SIZE];
int count;
while (!isInterrupted() && (count = inputStream.read(b)) > 0) {
outputStream.write(b, 0, count);
Expand Down