Stop backup jobs from continuing to run if backups become disabled.

Fixes #10819
fork-5.53.8
Cody Henthorne 2021-03-12 11:28:24 -05:00 zatwierdzone przez Greyson Parrelli
rodzic a64430c65f
commit a9149c5dc0
4 zmienionych plików z 50 dodań i 20 usunięć

Wyświetl plik

@ -79,11 +79,12 @@ public class FullBackupExporter extends FullBackupBase {
@NonNull AttachmentSecret attachmentSecret, @NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input, @NonNull SQLiteDatabase input,
@NonNull File output, @NonNull File output,
@NonNull String passphrase) @NonNull String passphrase,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException throws IOException
{ {
try (OutputStream outputStream = new FileOutputStream(output)) { try (OutputStream outputStream = new FileOutputStream(output)) {
internalExport(context, attachmentSecret, input, outputStream, passphrase, true); internalExport(context, attachmentSecret, input, outputStream, passphrase, true, cancellationSignal);
} }
} }
@ -92,11 +93,12 @@ public class FullBackupExporter extends FullBackupBase {
@NonNull AttachmentSecret attachmentSecret, @NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input, @NonNull SQLiteDatabase input,
@NonNull DocumentFile output, @NonNull DocumentFile output,
@NonNull String passphrase) @NonNull String passphrase,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException throws IOException
{ {
try (OutputStream outputStream = Objects.requireNonNull(context.getContentResolver().openOutputStream(output.getUri()))) { try (OutputStream outputStream = Objects.requireNonNull(context.getContentResolver().openOutputStream(output.getUri()))) {
internalExport(context, attachmentSecret, input, outputStream, passphrase, true); internalExport(context, attachmentSecret, input, outputStream, passphrase, true, cancellationSignal);
} }
} }
@ -107,7 +109,7 @@ public class FullBackupExporter extends FullBackupBase {
@NonNull String passphrase) @NonNull String passphrase)
throws IOException throws IOException
{ {
internalExport(context, attachmentSecret, input, outputStream, passphrase, false); internalExport(context, attachmentSecret, input, outputStream, passphrase, false, () -> false);
} }
private static void internalExport(@NonNull Context context, private static void internalExport(@NonNull Context context,
@ -115,7 +117,8 @@ public class FullBackupExporter extends FullBackupBase {
@NonNull SQLiteDatabase input, @NonNull SQLiteDatabase input,
@NonNull OutputStream fileOutputStream, @NonNull OutputStream fileOutputStream,
@NonNull String passphrase, @NonNull String passphrase,
boolean closeOutputStream) boolean closeOutputStream,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException throws IOException
{ {
BackupFrameOutputStream outputStream = new BackupFrameOutputStream(fileOutputStream, passphrase); BackupFrameOutputStream outputStream = new BackupFrameOutputStream(fileOutputStream, passphrase);
@ -129,23 +132,25 @@ public class FullBackupExporter extends FullBackupBase {
Stopwatch stopwatch = new Stopwatch("Backup"); Stopwatch stopwatch = new Stopwatch("Backup");
for (String table : tables) { for (String table : tables) {
throwIfCanceled(cancellationSignal);
if (table.equals(MmsDatabase.TABLE_NAME)) { if (table.equals(MmsDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMmsMessage, null, count); count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMmsMessage, null, count, cancellationSignal);
} else if (table.equals(SmsDatabase.TABLE_NAME)) { } else if (table.equals(SmsDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringSmsMessage, null, count); count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringSmsMessage, null, count, cancellationSignal);
} else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) { } else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count); count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count, cancellationSignal);
} else if (table.equals(AttachmentDatabase.TABLE_NAME)) { } else if (table.equals(AttachmentDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), cursor -> exportAttachment(attachmentSecret, cursor, outputStream), count); count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), cursor -> exportAttachment(attachmentSecret, cursor, outputStream), count, cancellationSignal);
} else if (table.equals(StickerDatabase.TABLE_NAME)) { } else if (table.equals(StickerDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, cursor -> true, cursor -> exportSticker(attachmentSecret, cursor, outputStream), count); count = exportTable(table, input, outputStream, cursor -> true, cursor -> exportSticker(attachmentSecret, cursor, outputStream), count, cancellationSignal);
} else if (!BLACKLISTED_TABLES.contains(table) && !table.startsWith("sqlite_")) { } else if (!BLACKLISTED_TABLES.contains(table) && !table.startsWith("sqlite_")) {
count = exportTable(table, input, outputStream, null, null, count); count = exportTable(table, input, outputStream, null, null, count, cancellationSignal);
} }
stopwatch.split("table::" + table); stopwatch.split("table::" + table);
} }
for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) { for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) {
throwIfCanceled(cancellationSignal);
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(preference); outputStream.write(preference);
} }
@ -153,6 +158,7 @@ public class FullBackupExporter extends FullBackupBase {
stopwatch.split("prefs"); stopwatch.split("prefs");
for (AvatarHelper.Avatar avatar : AvatarHelper.getAvatars(context)) { for (AvatarHelper.Avatar avatar : AvatarHelper.getAvatars(context)) {
throwIfCanceled(cancellationSignal);
if (avatar != null) { if (avatar != null) {
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(avatar.getFilename(), avatar.getInputStream(), avatar.getLength()); outputStream.write(avatar.getFilename(), avatar.getInputStream(), avatar.getLength());
@ -171,6 +177,12 @@ public class FullBackupExporter extends FullBackupBase {
} }
} }
private static void throwIfCanceled(@NonNull BackupCancellationSignal cancellationSignal) throws BackupCanceledException {
if (cancellationSignal.isCanceled()) {
throw new BackupCanceledException();
}
}
private static List<String> exportSchema(@NonNull SQLiteDatabase input, @NonNull BackupFrameOutputStream outputStream) private static List<String> exportSchema(@NonNull SQLiteDatabase input, @NonNull BackupFrameOutputStream outputStream)
throws IOException throws IOException
{ {
@ -201,18 +213,20 @@ public class FullBackupExporter extends FullBackupBase {
return tables; return tables;
} }
private static int exportTable(@NonNull String table, private static int exportTable(@NonNull String table,
@NonNull SQLiteDatabase input, @NonNull SQLiteDatabase input,
@NonNull BackupFrameOutputStream outputStream, @NonNull BackupFrameOutputStream outputStream,
@Nullable Predicate<Cursor> predicate, @Nullable Predicate<Cursor> predicate,
@Nullable Consumer<Cursor> postProcess, @Nullable Consumer<Cursor> postProcess,
int count) int count,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException throws IOException
{ {
String template = "INSERT INTO " + table + " VALUES "; String template = "INSERT INTO " + table + " VALUES ";
try (Cursor cursor = input.rawQuery("SELECT * FROM " + table, null)) { try (Cursor cursor = input.rawQuery("SELECT * FROM " + table, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
throwIfCanceled(cancellationSignal);
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
if (predicate == null || predicate.test(cursor)) { if (predicate == null || predicate.test(cursor)) {
@ -506,4 +520,10 @@ public class FullBackupExporter extends FullBackupBase {
outputStream.close(); outputStream.close();
} }
} }
public interface BackupCancellationSignal {
boolean isCanceled();
}
public static final class BackupCanceledException extends IOException { }
} }

Wyświetl plik

@ -115,12 +115,16 @@ public final class LocalBackupJob extends BaseJob {
AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(),
DatabaseFactory.getBackupDatabase(context), DatabaseFactory.getBackupDatabase(context),
tempFile, tempFile,
backupPassword); backupPassword,
this::isCanceled);
if (!tempFile.renameTo(backupFile)) { if (!tempFile.renameTo(backupFile)) {
Log.w(TAG, "Failed to rename temp file"); Log.w(TAG, "Failed to rename temp file");
throw new IOException("Renaming temporary backup file failed!"); throw new IOException("Renaming temporary backup file failed!");
} }
} catch (FullBackupExporter.BackupCanceledException e) {
Log.w(TAG, "Backup cancelled");
throw e;
} catch (IOException e) { } catch (IOException e) {
BackupFileIOError.postNotificationForException(context, e, getRunAttempt()); BackupFileIOError.postNotificationForException(context, e, getRunAttempt());
throw e; throw e;

Wyświetl plik

@ -109,12 +109,16 @@ public final class LocalBackupJobApi29 extends BaseJob {
AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(),
DatabaseFactory.getBackupDatabase(context), DatabaseFactory.getBackupDatabase(context),
temporaryFile, temporaryFile,
backupPassword); backupPassword,
this::isCanceled);
if (!temporaryFile.renameTo(fileName)) { if (!temporaryFile.renameTo(fileName)) {
Log.w(TAG, "Failed to rename temp file"); Log.w(TAG, "Failed to rename temp file");
throw new IOException("Renaming temporary backup file failed!"); throw new IOException("Renaming temporary backup file failed!");
} }
} catch (FullBackupExporter.BackupCanceledException e) {
Log.w(TAG, "Backup cancelled");
throw e;
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, "Error during backup!", e); Log.w(TAG, "Error during backup!", e);
BackupFileIOError.postNotificationForException(context, e, getRunAttempt()); BackupFileIOError.postNotificationForException(context, e, getRunAttempt());

Wyświetl plik

@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.backup.BackupDialog; import org.thoughtcrime.securesms.backup.BackupDialog;
import org.thoughtcrime.securesms.backup.FullBackupBase; import org.thoughtcrime.securesms.backup.FullBackupBase;
import org.thoughtcrime.securesms.database.NoExternalStorageException; import org.thoughtcrime.securesms.database.NoExternalStorageException;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.LocalBackupJob; import org.thoughtcrime.securesms.jobs.LocalBackupJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.permissions.Permissions;
@ -250,5 +251,6 @@ public class BackupsPreferenceFragment extends Fragment {
create.setVisibility(View.GONE); create.setVisibility(View.GONE);
folder.setVisibility(View.GONE); folder.setVisibility(View.GONE);
verify.setVisibility(View.GONE); verify.setVisibility(View.GONE);
ApplicationDependencies.getJobManager().cancelAllInQueue(LocalBackupJob.QUEUE);
} }
} }