From 7b95256ba524e5628420fc0a7da34a530cff499c Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 14 Jun 2019 14:14:14 -0300 Subject: [PATCH] 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. --- .../service/backup/BackupJobService.java | 4 -- .../backup/service/backup/BackupObserver.java | 5 +++ .../backup/service/backup/BackupService.java | 5 --- .../backup/session/backup/BackupMonitor.java | 20 ++++++++++ .../backup/session/backup/BackupSession.java | 2 +- .../ConfigurableBackupTransport.java | 33 ++++++---------- .../transport/component/BackupComponent.java | 6 +-- .../ContentProviderBackupComponent.java | 38 ++++++++++++------- .../provider/ContentProviderBackupState.java | 21 +++------- 9 files changed, 72 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/com/stevesoltys/backup/session/backup/BackupMonitor.java diff --git a/app/src/main/java/com/stevesoltys/backup/service/backup/BackupJobService.java b/app/src/main/java/com/stevesoltys/backup/service/backup/BackupJobService.java index 3d73f9be..cd0d2d78 100644 --- a/app/src/main/java/com/stevesoltys/backup/service/backup/BackupJobService.java +++ b/app/src/main/java/com/stevesoltys/backup/service/backup/BackupJobService.java @@ -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 "); diff --git a/app/src/main/java/com/stevesoltys/backup/service/backup/BackupObserver.java b/app/src/main/java/com/stevesoltys/backup/service/backup/BackupObserver.java index 8bcbfefa..a319b377 100644 --- a/app/src/main/java/com/stevesoltys/backup/service/backup/BackupObserver.java +++ b/app/src/main/java/com/stevesoltys/backup/service/backup/BackupObserver.java @@ -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(); diff --git a/app/src/main/java/com/stevesoltys/backup/service/backup/BackupService.java b/app/src/main/java/com/stevesoltys/backup/service/backup/BackupService.java index 779bcdd1..132b082f 100644 --- a/app/src/main/java/com/stevesoltys/backup/service/backup/BackupService.java +++ b/app/src/main/java/com/stevesoltys/backup/service/backup/BackupService.java @@ -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); diff --git a/app/src/main/java/com/stevesoltys/backup/session/backup/BackupMonitor.java b/app/src/main/java/com/stevesoltys/backup/session/backup/BackupMonitor.java new file mode 100644 index 00000000..a8d264f0 --- /dev/null +++ b/app/src/main/java/com/stevesoltys/backup/session/backup/BackupMonitor.java @@ -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, "?")); + } + +} diff --git a/app/src/main/java/com/stevesoltys/backup/session/backup/BackupSession.java b/app/src/main/java/com/stevesoltys/backup/session/backup/BackupSession.java index dfd95173..96cd2953 100644 --- a/app/src/main/java/com/stevesoltys/backup/session/backup/BackupSession.java +++ b/app/src/main/java/com/stevesoltys/backup/session/backup/BackupSession.java @@ -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 { diff --git a/app/src/main/java/com/stevesoltys/backup/transport/ConfigurableBackupTransport.java b/app/src/main/java/com/stevesoltys/backup/transport/ConfigurableBackupTransport.java index 811fb97c..b709bb13 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/ConfigurableBackupTransport.java +++ b/app/src/main/java/com/stevesoltys/backup/transport/ConfigurableBackupTransport.java @@ -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(); diff --git a/app/src/main/java/com/stevesoltys/backup/transport/component/BackupComponent.java b/app/src/main/java/com/stevesoltys/backup/transport/component/BackupComponent.java index f3c7cd42..92715d61 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/component/BackupComponent.java +++ b/app/src/main/java/com/stevesoltys/backup/transport/component/BackupComponent.java @@ -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(); } diff --git a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java index 2f6a677c..bab06921 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java +++ b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java @@ -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(); diff --git a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupState.java b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupState.java index 799874b4..0adc8d17 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupState.java +++ b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupState.java @@ -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; } }