Fix detection of the end of backup

For the current transport it is important to know when the backup ends,
because it resets its state only then and closes the ZIP file.

The detection was broken,
because some packages didn't have data to back up (LOG_EVENT_ID_NO_DATA_TO_SEND),
so the transport's methods weren't called and the package counter not updated.

The hacky solution is to use the BackupObserver to call back into the
transport at the end of backup.
Ideally, future transports won't need to know when the backup finishes.
This commit is contained in:
Torsten Grote 2019-06-14 14:14:14 -03:00
parent 84d91290ac
commit 7b95256ba5
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
9 changed files with 72 additions and 62 deletions

View file

@ -9,12 +9,10 @@ import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import com.stevesoltys.backup.service.PackageService; import com.stevesoltys.backup.service.PackageService;
import com.stevesoltys.backup.transport.ConfigurableBackupTransport;
import com.stevesoltys.backup.transport.ConfigurableBackupTransportService; import com.stevesoltys.backup.transport.ConfigurableBackupTransportService;
import static android.app.backup.BackupManager.FLAG_NON_INCREMENTAL_BACKUP; import static android.app.backup.BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
import static android.os.ServiceManager.getService; import static android.os.ServiceManager.getService;
import static com.stevesoltys.backup.transport.ConfigurableBackupTransportService.getBackupTransport;
public class BackupJobService extends JobService { public class BackupJobService extends JobService {
@ -34,8 +32,6 @@ public class BackupJobService extends JobService {
try { try {
String[] packages = packageService.getEligiblePackages(); String[] packages = packageService.getEligiblePackages();
// TODO use an observer to know when backups fail // TODO use an observer to know when backups fail
ConfigurableBackupTransport backupTransport = getBackupTransport(getApplication());
backupTransport.prepareBackup(packages.length);
int result = backupManager.requestBackup(packages, null, null, FLAG_NON_INCREMENTAL_BACKUP); int result = backupManager.requestBackup(packages, null, null, FLAG_NON_INCREMENTAL_BACKUP);
if (result == BackupManager.SUCCESS) { if (result == BackupManager.SUCCESS) {
Log.i(TAG, "Backup succeeded "); Log.i(TAG, "Backup succeeded ");

View file

@ -12,6 +12,8 @@ import com.stevesoltys.backup.session.backup.BackupResult;
import com.stevesoltys.backup.session.backup.BackupSession; import com.stevesoltys.backup.session.backup.BackupSession;
import com.stevesoltys.backup.session.backup.BackupSessionObserver; import com.stevesoltys.backup.session.backup.BackupSessionObserver;
import static com.stevesoltys.backup.transport.ConfigurableBackupTransportService.getBackupTransport;
/** /**
* @author Steve Soltys * @author Steve Soltys
*/ */
@ -59,6 +61,9 @@ class BackupObserver implements BackupSessionObserver {
@Override @Override
public void backupSessionCompleted(BackupSession backupSession, BackupResult backupResult) { public void backupSessionCompleted(BackupSession backupSession, BackupResult backupResult) {
if (backupResult == BackupResult.SUCCESS) getBackupTransport(context).backupFinished();
context.runOnUiThread(() -> { context.runOnUiThread(() -> {
if (backupResult == BackupResult.SUCCESS) { if (backupResult == BackupResult.SUCCESS) {
Toast.makeText(context, R.string.backup_success, Toast.LENGTH_LONG).show(); Toast.makeText(context, R.string.backup_success, Toast.LENGTH_LONG).show();

View file

@ -11,12 +11,9 @@ import com.stevesoltys.backup.activity.PopupWindowUtil;
import com.stevesoltys.backup.activity.backup.BackupPopupWindowListener; import com.stevesoltys.backup.activity.backup.BackupPopupWindowListener;
import com.stevesoltys.backup.service.TransportService; import com.stevesoltys.backup.service.TransportService;
import com.stevesoltys.backup.session.backup.BackupSession; import com.stevesoltys.backup.session.backup.BackupSession;
import com.stevesoltys.backup.transport.ConfigurableBackupTransport;
import java.util.Set; import java.util.Set;
import static com.stevesoltys.backup.transport.ConfigurableBackupTransportService.getBackupTransport;
/** /**
* @author Steve Soltys * @author Steve Soltys
*/ */
@ -34,8 +31,6 @@ public class BackupService {
PopupWindow popupWindow = PopupWindowUtil.showLoadingPopupWindow(parent); PopupWindow popupWindow = PopupWindowUtil.showLoadingPopupWindow(parent);
BackupObserver backupObserver = new BackupObserver(parent, popupWindow); BackupObserver backupObserver = new BackupObserver(parent, popupWindow);
ConfigurableBackupTransport backupTransport = getBackupTransport(parent.getApplication());
backupTransport.prepareBackup(selectedPackages.size());
BackupSession backupSession = transportService.backup(backupObserver, selectedPackages); BackupSession backupSession = transportService.backup(backupObserver, selectedPackages);
View popupWindowButton = popupWindow.getContentView().findViewById(R.id.popup_cancel_button); View popupWindowButton = popupWindow.getContentView().findViewById(R.id.popup_cancel_button);

View file

@ -0,0 +1,20 @@
package com.stevesoltys.backup.session.backup;
import android.app.backup.IBackupManagerMonitor;
import android.os.Bundle;
import android.util.Log;
import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY;
import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_ID;
import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME;
class BackupMonitor extends IBackupManagerMonitor.Stub {
@Override
public void onEvent(Bundle bundle) {
Log.d("BackupMonitor", "ID: " + bundle.getInt(EXTRA_LOG_EVENT_ID));
Log.d("BackupMonitor", "CATEGORY: " + bundle.getInt(EXTRA_LOG_EVENT_CATEGORY, -1));
Log.d("BackupMonitor", "PACKAGE: " + bundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME, "?"));
}
}

View file

@ -30,7 +30,7 @@ public class BackupSession extends IBackupObserver.Stub {
public void start() throws RemoteException { public void start() throws RemoteException {
String [] selectedPackageArray = packages.toArray(new String[0]); String [] selectedPackageArray = packages.toArray(new String[0]);
backupManager.requestBackup(selectedPackageArray, this, null, FLAG_NON_INCREMENTAL_BACKUP); backupManager.requestBackup(selectedPackageArray, this, new BackupMonitor(), FLAG_NON_INCREMENTAL_BACKUP);
} }
public void stop(BackupResult result) throws RemoteException { public void stop(BackupResult result) throws RemoteException {

View file

@ -36,10 +36,6 @@ public class ConfigurableBackupTransport extends BackupTransport {
restoreComponent = new ContentProviderRestoreComponent(context); restoreComponent = new ContentProviderRestoreComponent(context);
} }
public void prepareBackup(int numberOfPackages) {
backupComponent.prepareBackup(numberOfPackages);
}
public void prepareRestore(String password, Uri fileUri) { public void prepareRestore(String password, Uri fileUri) {
restoreComponent.prepareRestore(password, fileUri); restoreComponent.prepareRestore(password, fileUri);
} }
@ -63,20 +59,7 @@ public class ConfigurableBackupTransport extends BackupTransport {
@Override @Override
public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup) { public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup) {
// TODO re-include key-value (incremental) return true;
// affected apps:
// * com.android.documentsui
// * android
// * com.android.nfc
// * com.android.calendar
// * com.android.providers.settings
// * com.android.cellbroadcastreceiver
// * com.android.calllogbackup
// * com.android.providers.blockednumber
// * com.android.providers.userdictionary
if (isFullBackup) return true;
Log.i(TAG, "Excluding key-value backup of " + targetPackage.packageName);
return false;
} }
@Override @Override
@ -99,15 +82,17 @@ public class ConfigurableBackupTransport extends BackupTransport {
return backupComponent.currentDestinationString(); return backupComponent.currentDestinationString();
} }
/* Methods related to Backup */
@Override @Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) { public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) {
// TODO handle flags return backupComponent.performIncrementalBackup(packageInfo, inFd, flags);
return performBackup(packageInfo, inFd);
} }
@Override @Override
public int performBackup(PackageInfo targetPackage, ParcelFileDescriptor fileDescriptor) { public int performBackup(PackageInfo targetPackage, ParcelFileDescriptor fileDescriptor) {
return backupComponent.performIncrementalBackup(targetPackage, fileDescriptor); Log.w(TAG, "Warning: Legacy performBackup() method called.");
return performBackup(targetPackage, fileDescriptor, 0);
} }
@Override @Override
@ -156,6 +141,12 @@ public class ConfigurableBackupTransport extends BackupTransport {
return backupComponent.clearBackupData(packageInfo); return backupComponent.clearBackupData(packageInfo);
} }
public void backupFinished() {
backupComponent.backupFinished();
}
/* Methods related to Restore */
@Override @Override
public long getCurrentRestoreSet() { public long getCurrentRestoreSet() {
return restoreComponent.getCurrentRestoreSet(); return restoreComponent.getCurrentRestoreSet();

View file

@ -8,8 +8,6 @@ import android.os.ParcelFileDescriptor;
*/ */
public interface BackupComponent { public interface BackupComponent {
void prepareBackup(int numberOfPackages);
String currentDestinationString(); String currentDestinationString();
String dataManagementLabel(); String dataManagementLabel();
@ -20,7 +18,7 @@ public interface BackupComponent {
int finishBackup(); int finishBackup();
int performIncrementalBackup(PackageInfo targetPackage, ParcelFileDescriptor data); int performIncrementalBackup(PackageInfo targetPackage, ParcelFileDescriptor data, int flags);
int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor fileDescriptor); int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor fileDescriptor);
@ -35,4 +33,6 @@ public interface BackupComponent {
long requestBackupTime(); long requestBackupTime();
long requestFullBackupTime(); long requestFullBackupTime();
void backupFinished();
} }

View file

@ -31,7 +31,10 @@ import javax.crypto.SecretKey;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import static android.app.backup.BackupTransport.FLAG_INCREMENTAL;
import static android.app.backup.BackupTransport.FLAG_NON_INCREMENTAL;
import static android.app.backup.BackupTransport.TRANSPORT_ERROR; import static android.app.backup.BackupTransport.TRANSPORT_ERROR;
import static android.app.backup.BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED;
import static android.app.backup.BackupTransport.TRANSPORT_OK; import static android.app.backup.BackupTransport.TRANSPORT_OK;
import static android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED; import static android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED;
import static android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED; import static android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
@ -49,7 +52,7 @@ import static java.util.Objects.requireNonNull;
*/ */
public class ContentProviderBackupComponent implements BackupComponent { public class ContentProviderBackupComponent implements BackupComponent {
private static final String TAG = ContentProviderBackupComponent.class.getName(); private static final String TAG = ContentProviderBackupComponent.class.getSimpleName();
private static final String DOCUMENT_SUFFIX = "yyyy-MM-dd_HH_mm_ss"; private static final String DOCUMENT_SUFFIX = "yyyy-MM-dd_HH_mm_ss";
@ -61,8 +64,6 @@ public class ContentProviderBackupComponent implements BackupComponent {
private final Context context; private final Context context;
private int numberOfPackages = 0;
private ContentProviderBackupState backupState; private ContentProviderBackupState backupState;
public ContentProviderBackupComponent(Context context) { public ContentProviderBackupComponent(Context context) {
@ -93,11 +94,6 @@ public class ContentProviderBackupComponent implements BackupComponent {
return TRANSPORT_OK; return TRANSPORT_OK;
} }
@Override
public void prepareBackup(int numberOfPackages) {
this.numberOfPackages = numberOfPackages;
}
@Override @Override
public String currentDestinationString() { public String currentDestinationString() {
return DESTINATION_DESCRIPTION; return DESTINATION_DESCRIPTION;
@ -133,7 +129,6 @@ public class ContentProviderBackupComponent implements BackupComponent {
try { try {
initializeBackupState(); initializeBackupState();
backupState.setPackageIndex(backupState.getPackageIndex() + 1);
backupState.setPackageName(targetPackage.packageName); backupState.setPackageName(targetPackage.packageName);
backupState.setInputFileDescriptor(fileDescriptor); backupState.setInputFileDescriptor(fileDescriptor);
@ -156,12 +151,24 @@ public class ContentProviderBackupComponent implements BackupComponent {
} }
@Override @Override
public int performIncrementalBackup(PackageInfo packageInfo, ParcelFileDescriptor data) { public int performIncrementalBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
if (isIncremental) {
Log.w(TAG, "Can not handle incremental backup. Requesting non-incremental for " + packageInfo.packageName);
return TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED;
}
boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
if (isNonIncremental) {
Log.i(TAG, "Performing non-incremental backup for " + packageInfo.packageName);
} else {
Log.i(TAG, "Performing backup for " + packageInfo.packageName);
}
BackupDataInput backupDataInput = new BackupDataInput(data.getFileDescriptor()); BackupDataInput backupDataInput = new BackupDataInput(data.getFileDescriptor());
try { try {
initializeBackupState(); initializeBackupState();
backupState.setPackageIndex(backupState.getPackageIndex() + 1);
backupState.setPackageName(packageInfo.packageName); backupState.setPackageName(packageInfo.packageName);
return transferIncrementalBackupData(backupDataInput); return transferIncrementalBackupData(backupDataInput);
@ -261,6 +268,11 @@ public class ContentProviderBackupComponent implements BackupComponent {
return TRANSPORT_OK; return TRANSPORT_OK;
} }
@Override
public void backupFinished() {
clearBackupState(true);
}
private void initializeBackupState() throws Exception { private void initializeBackupState() throws Exception {
if (backupState == null) { if (backupState == null) {
backupState = new ContentProviderBackupState(); backupState = new ContentProviderBackupState();
@ -333,8 +345,8 @@ public class ContentProviderBackupComponent implements BackupComponent {
outputStream.closeEntry(); outputStream.closeEntry();
} }
if (closeFile) {
if (backupState.getPackageIndex() == numberOfPackages || closeFile) { Log.d(TAG, "Closing backup file...");
if (outputStream != null) { if (outputStream != null) {
outputStream.finish(); outputStream.finish();
outputStream.close(); outputStream.close();

View file

@ -2,12 +2,13 @@ package com.stevesoltys.backup.transport.component.provider;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import java.io.InputStream; import java.io.InputStream;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
/** /**
* @author Steve Soltys * @author Steve Soltys
*/ */
@ -29,13 +30,11 @@ class ContentProviderBackupState {
private String packageName; private String packageName;
private int packageIndex;
private byte[] salt; private byte[] salt;
private SecretKey secretKey; private SecretKey secretKey;
public ContentProviderBackupState() { ContentProviderBackupState() {
salt = new byte[16]; salt = new byte[16];
SECURE_RANDOM.nextBytes(salt); SECURE_RANDOM.nextBytes(salt);
} }
@ -88,14 +87,6 @@ class ContentProviderBackupState {
this.outputStream = outputStream; this.outputStream = outputStream;
} }
int getPackageIndex() {
return packageIndex;
}
void setPackageIndex(int packageIndex) {
this.packageIndex = packageIndex;
}
String getPackageName() { String getPackageName() {
return packageName; return packageName;
} }
@ -108,11 +99,11 @@ class ContentProviderBackupState {
return salt; return salt;
} }
public SecretKey getSecretKey() { SecretKey getSecretKey() {
return secretKey; return secretKey;
} }
public void setSecretKey(SecretKey secretKey) { void setSecretKey(SecretKey secretKey) {
this.secretKey = secretKey; this.secretKey = secretKey;
} }
} }