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:
parent
84d91290ac
commit
7b95256ba5
9 changed files with 72 additions and 62 deletions
|
@ -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 ");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, "?"));
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue