Greyson Parrelli 18756aedf6 Remove the ability to save backups to the external SD card.
The directory we were previously saving backups to on the external SD
card is actually deleted upon app uninstall and/or clearing the app's
data. There's also no reliable way to write to the root of an external
SD card (that isn't comically inconvenient), so for now it's safer if we
just move back to getting the regular 'ol standard external storage
directory (which is likely internal storage, despite its name).

Fixes #7845
2018-06-29 14:10:45 -07:00

161 lines
4.7 KiB
Java

package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.NoExternalStorageException;
import org.whispersystems.libsignal.util.ByteUtil;
import java.io.File;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Locale;
public class BackupUtil {
private static final String TAG = BackupUtil.class.getSimpleName();
public static @NonNull String getLastBackupTime(@NonNull Context context, @NonNull Locale locale) {
try {
BackupInfo backup = getLatestBackup();
if (backup == null) return context.getString(R.string.BackupUtil_never);
else return DateUtils.getExtendedRelativeTimeSpanString(context, locale, backup.getTimestamp());
} catch (NoExternalStorageException e) {
Log.w(TAG, e);
return context.getString(R.string.BackupUtil_unknown);
}
}
public static @Nullable BackupInfo getLatestBackup() throws NoExternalStorageException {
File backupDirectory = StorageUtil.getBackupDirectory();
File[] backups = backupDirectory.listFiles();
BackupInfo latestBackup = null;
for (File backup : backups) {
long backupTimestamp = getBackupTimestamp(backup);
if (latestBackup == null || (backupTimestamp != -1 && backupTimestamp > latestBackup.getTimestamp())) {
latestBackup = new BackupInfo(backupTimestamp, backup.length(), backup);
}
}
return latestBackup;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public static void deleteAllBackups() {
try {
File backupDirectory = StorageUtil.getBackupDirectory();
File[] backups = backupDirectory.listFiles();
for (File backup : backups) {
if (backup.isFile()) backup.delete();
}
} catch (NoExternalStorageException e) {
Log.w(TAG, e);
}
}
public static void deleteOldBackups() {
try {
File backupDirectory = StorageUtil.getBackupDirectory();
File[] backups = backupDirectory.listFiles();
if (backups != null && backups.length > 2) {
Arrays.sort(backups, (left, right) -> {
long leftTimestamp = getBackupTimestamp(left);
long rightTimestamp = getBackupTimestamp(right);
if (leftTimestamp == -1 && rightTimestamp == -1) return 0;
else if (leftTimestamp == -1) return 1;
else if (rightTimestamp == -1) return -1;
return (int)(rightTimestamp - leftTimestamp);
});
for (int i=2;i<backups.length;i++) {
Log.w(TAG, "Deleting: " + backups[i].getAbsolutePath());
if (!backups[i].delete()) {
Log.w(TAG, "Delete failed: " + backups[i].getAbsolutePath());
}
}
}
} catch (NoExternalStorageException e) {
Log.w(TAG, e);
}
}
public static @NonNull String[] generateBackupPassphrase() {
String[] result = new String[6];
byte[] random = new byte[30];
new SecureRandom().nextBytes(random);
for (int i=0;i<30;i+=5) {
result[i/5] = String.format("%05d", ByteUtil.byteArray5ToLong(random, i) % 100000);
}
return result;
}
private static long getBackupTimestamp(File backup) {
String name = backup.getName();
String[] prefixSuffix = name.split("[.]");
if (prefixSuffix.length == 2) {
String[] parts = prefixSuffix[0].split("\\-");
if (parts.length == 7) {
try {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, Integer.parseInt(parts[1]));
calendar.set(Calendar.MONTH, Integer.parseInt(parts[2]) - 1);
calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parts[3]));
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parts[4]));
calendar.set(Calendar.MINUTE, Integer.parseInt(parts[5]));
calendar.set(Calendar.SECOND, Integer.parseInt(parts[6]));
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis();
} catch (NumberFormatException e) {
Log.w(TAG, e);
}
}
}
return -1;
}
public static class BackupInfo {
private final long timestamp;
private final long size;
private final File file;
BackupInfo(long timestamp, long size, File file) {
this.timestamp = timestamp;
this.size = size;
this.file = file;
}
public long getTimestamp() {
return timestamp;
}
public long getSize() {
return size;
}
public File getFile() {
return file;
}
}
}