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 com.stevesoltys.backup.service.PackageService;
import com.stevesoltys.backup.transport.ConfigurableBackupTransport;
import com.stevesoltys.backup.transport.ConfigurableBackupTransportService;
import static android.app.backup.BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
import static android.os.ServiceManager.getService;
import static com.stevesoltys.backup.transport.ConfigurableBackupTransportService.getBackupTransport;
public class BackupJobService extends JobService {
@ -34,8 +32,6 @@ public class BackupJobService extends JobService {
try {
String[] packages = packageService.getEligiblePackages();
// 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);
if (result == BackupManager.SUCCESS) {
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.BackupSessionObserver;
import static com.stevesoltys.backup.transport.ConfigurableBackupTransportService.getBackupTransport;
/**
* @author Steve Soltys
*/
@ -59,6 +61,9 @@ class BackupObserver implements BackupSessionObserver {
@Override
public void backupSessionCompleted(BackupSession backupSession, BackupResult backupResult) {
if (backupResult == BackupResult.SUCCESS) getBackupTransport(context).backupFinished();
context.runOnUiThread(() -> {
if (backupResult == BackupResult.SUCCESS) {
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.service.TransportService;
import com.stevesoltys.backup.session.backup.BackupSession;
import com.stevesoltys.backup.transport.ConfigurableBackupTransport;
import java.util.Set;
import static com.stevesoltys.backup.transport.ConfigurableBackupTransportService.getBackupTransport;
/**
* @author Steve Soltys
*/
@ -34,8 +31,6 @@ public class BackupService {
PopupWindow popupWindow = PopupWindowUtil.showLoadingPopupWindow(parent);
BackupObserver backupObserver = new BackupObserver(parent, popupWindow);
ConfigurableBackupTransport backupTransport = getBackupTransport(parent.getApplication());
backupTransport.prepareBackup(selectedPackages.size());
BackupSession backupSession = transportService.backup(backupObserver, selectedPackages);
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 {
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 {

View file

@ -36,10 +36,6 @@ public class ConfigurableBackupTransport extends BackupTransport {
restoreComponent = new ContentProviderRestoreComponent(context);
}
public void prepareBackup(int numberOfPackages) {
backupComponent.prepareBackup(numberOfPackages);
}
public void prepareRestore(String password, Uri fileUri) {
restoreComponent.prepareRestore(password, fileUri);
}
@ -63,20 +59,7 @@ public class ConfigurableBackupTransport extends BackupTransport {
@Override
public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup) {
// TODO re-include key-value (incremental)
// 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;
return true;
}
@Override
@ -99,15 +82,17 @@ public class ConfigurableBackupTransport extends BackupTransport {
return backupComponent.currentDestinationString();
}
/* Methods related to Backup */
@Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) {
// TODO handle flags
return performBackup(packageInfo, inFd);
return backupComponent.performIncrementalBackup(packageInfo, inFd, flags);
}
@Override
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
@ -156,6 +141,12 @@ public class ConfigurableBackupTransport extends BackupTransport {
return backupComponent.clearBackupData(packageInfo);
}
public void backupFinished() {
backupComponent.backupFinished();
}
/* Methods related to Restore */
@Override
public long getCurrentRestoreSet() {
return restoreComponent.getCurrentRestoreSet();

View file

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

View file

@ -31,7 +31,10 @@ import javax.crypto.SecretKey;
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_NON_INCREMENTAL_BACKUP_REQUIRED;
import static android.app.backup.BackupTransport.TRANSPORT_OK;
import static android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED;
import static android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
@ -49,7 +52,7 @@ import static java.util.Objects.requireNonNull;
*/
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";
@ -61,8 +64,6 @@ public class ContentProviderBackupComponent implements BackupComponent {
private final Context context;
private int numberOfPackages = 0;
private ContentProviderBackupState backupState;
public ContentProviderBackupComponent(Context context) {
@ -93,11 +94,6 @@ public class ContentProviderBackupComponent implements BackupComponent {
return TRANSPORT_OK;
}
@Override
public void prepareBackup(int numberOfPackages) {
this.numberOfPackages = numberOfPackages;
}
@Override
public String currentDestinationString() {
return DESTINATION_DESCRIPTION;
@ -133,7 +129,6 @@ public class ContentProviderBackupComponent implements BackupComponent {
try {
initializeBackupState();
backupState.setPackageIndex(backupState.getPackageIndex() + 1);
backupState.setPackageName(targetPackage.packageName);
backupState.setInputFileDescriptor(fileDescriptor);
@ -156,12 +151,24 @@ public class ContentProviderBackupComponent implements BackupComponent {
}
@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());
try {
initializeBackupState();
backupState.setPackageIndex(backupState.getPackageIndex() + 1);
backupState.setPackageName(packageInfo.packageName);
return transferIncrementalBackupData(backupDataInput);
@ -261,6 +268,11 @@ public class ContentProviderBackupComponent implements BackupComponent {
return TRANSPORT_OK;
}
@Override
public void backupFinished() {
clearBackupState(true);
}
private void initializeBackupState() throws Exception {
if (backupState == null) {
backupState = new ContentProviderBackupState();
@ -333,8 +345,8 @@ public class ContentProviderBackupComponent implements BackupComponent {
outputStream.closeEntry();
}
if (backupState.getPackageIndex() == numberOfPackages || closeFile) {
if (closeFile) {
Log.d(TAG, "Closing backup file...");
if (outputStream != null) {
outputStream.finish();
outputStream.close();

View file

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