diff --git a/src/org/thoughtcrime/securesms/attachments/AttachmentId.java b/src/org/thoughtcrime/securesms/attachments/AttachmentId.java index 3d43c8419..d9bdff3f5 100644 --- a/src/org/thoughtcrime/securesms/attachments/AttachmentId.java +++ b/src/org/thoughtcrime/securesms/attachments/AttachmentId.java @@ -32,7 +32,7 @@ public class AttachmentId { } public @NonNull String toString() { - return "(row id: " + rowId + ", unique ID: " + uniqueId + ")"; + return "AttachmentId::(" + rowId + ", " + uniqueId + ")"; } public boolean isValid() { diff --git a/src/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/src/org/thoughtcrime/securesms/backup/FullBackupExporter.java index 1f3f0f463..ac7f09d82 100644 --- a/src/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/src/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupReceiptDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsColumns; @@ -31,6 +32,7 @@ import org.thoughtcrime.securesms.database.StickerDatabase; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.util.Conversions; +import org.thoughtcrime.securesms.util.Stopwatch; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.kdf.HKDFv3; import org.whispersystems.libsignal.util.ByteUtil; @@ -73,6 +75,8 @@ public class FullBackupExporter extends FullBackupBase { List tables = exportSchema(input, outputStream); int count = 0; + Stopwatch stopwatch = new Stopwatch("Backup"); + for (String table : tables) { if (table.equals(MmsDatabase.TABLE_NAME)) { count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMessage, null, count); @@ -91,6 +95,7 @@ public class FullBackupExporter extends FullBackupBase { { count = exportTable(table, input, outputStream, null, null, count); } + stopwatch.split("table::" + table); } for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) { @@ -98,11 +103,16 @@ public class FullBackupExporter extends FullBackupBase { outputStream.write(preference); } + stopwatch.split("prefs"); + for (File avatar : AvatarHelper.getAvatarFiles(context)) { EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); outputStream.write(avatar.getName(), new FileInputStream(avatar), avatar.length()); } + stopwatch.split("avatars"); + stopwatch.stop(TAG); + outputStream.writeEnd(); outputStream.close(); EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, ++count)); @@ -201,8 +211,14 @@ public class FullBackupExporter extends FullBackupBase { String data = cursor.getString(cursor.getColumnIndexOrThrow(AttachmentDatabase.DATA)); byte[] random = cursor.getBlob(cursor.getColumnIndexOrThrow(AttachmentDatabase.DATA_RANDOM)); - if (!TextUtils.isEmpty(data) && size <= 0) { - size = calculateVeryOldStreamLength(attachmentSecret, random, data); + if (!TextUtils.isEmpty(data)) { + long fileLength = new File(data).length(); + long dbLength = size; + + if (size <= 0 || fileLength != dbLength) { + size = calculateVeryOldStreamLength(attachmentSecret, random, data); + Log.w(TAG, "Needed size calculation! Manual: " + size + " File: " + fileLength + " DB: " + dbLength + " ID: " + new AttachmentId(rowId, uniqueId)); + } } if (!TextUtils.isEmpty(data) && size > 0) { @@ -331,7 +347,9 @@ public class FullBackupExporter extends FullBackupBase { .build()) .build()); - writeStream(in); + if (writeStream(in) != size) { + throw new IOException("Size mismatch!"); + } } public void write(@NonNull AttachmentId attachmentId, @NonNull InputStream in, long size) throws IOException { @@ -343,7 +361,9 @@ public class FullBackupExporter extends FullBackupBase { .build()) .build()); - writeStream(in); + if (writeStream(in) != size) { + throw new IOException("Size mismatch!"); + } } public void writeSticker(long rowId, @NonNull InputStream in, long size) throws IOException { @@ -354,7 +374,9 @@ public class FullBackupExporter extends FullBackupBase { .build()) .build()); - writeStream(in); + if (writeStream(in) != size) { + throw new IOException("Size mismatch!"); + } } void writeDatabaseVersion(int version) throws IOException { @@ -367,13 +389,18 @@ public class FullBackupExporter extends FullBackupBase { write(outputStream, BackupProtos.BackupFrame.newBuilder().setEnd(true).build()); } - private void writeStream(@NonNull InputStream inputStream) throws IOException { + /** + * @return The amount of data written from the provided InputStream. + */ + private long writeStream(@NonNull InputStream inputStream) throws IOException { try { Conversions.intToByteArray(iv, 0, counter++); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv)); mac.update(iv); byte[] buffer = new byte[8192]; + long total = 0; + int read; while ((read = inputStream.read(buffer)) != -1) { @@ -383,6 +410,8 @@ public class FullBackupExporter extends FullBackupBase { outputStream.write(ciphertext); mac.update(ciphertext); } + + total += read; } byte[] remainder = cipher.doFinal(); @@ -391,6 +420,8 @@ public class FullBackupExporter extends FullBackupBase { byte[] attachmentDigest = mac.doFinal(); outputStream.write(attachmentDigest, 0, 10); + + return total; } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { throw new AssertionError(e); }