diff --git a/api/system-current.txt b/api/system-current.txt index 95b85d95de806..f530584f91698 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6738,6 +6738,7 @@ package android.app.backup { method public boolean isBackupEnabled(); method public java.lang.String[] listAllTransports(); method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver); + method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver, int); method public int requestRestore(android.app.backup.RestoreObserver); method public java.lang.String selectBackupTransport(java.lang.String); method public void setAutoRestore(boolean); @@ -6748,6 +6749,8 @@ package android.app.backup { field public static final int ERROR_TRANSPORT_ABORTED = -1000; // 0xfffffc18 field public static final int ERROR_TRANSPORT_PACKAGE_REJECTED = -1002; // 0xfffffc16 field public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED = -1005; // 0xfffffc13 + field public static final int FLAG_NON_INCREMENTAL_BACKUP = 1; // 0x1 + field public static final java.lang.String PACKAGE_MANAGER_SENTINEL = "@pm@"; field public static final int SUCCESS = 0; // 0x0 } diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 5bf80765a7dec..780db5e26c35d 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -270,7 +270,7 @@ public final class Bmgr { } } - private void backupNowAllPackages() { + private void backupNowAllPackages(boolean nonIncrementalBackup) { int userId = UserHandle.USER_SYSTEM; IPackageManager mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); @@ -297,14 +297,19 @@ public final class Bmgr { System.err.println(BMGR_NOT_RUNNING_ERR); } } - backupNowPackages(packages); + backupNowPackages(packages, nonIncrementalBackup); } } - private void backupNowPackages(List packages) { + private void backupNowPackages(List packages, boolean nonIncrementalBackup) { + int flags = 0; + if (nonIncrementalBackup) { + flags |= BackupManager.FLAG_NON_INCREMENTAL_BACKUP; + } try { BackupObserver observer = new BackupObserver(); - int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer); + int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer, + flags); if (err == 0) { // Off and running -- wait for the backup to complete observer.waitForCompletion(); @@ -320,24 +325,31 @@ public final class Bmgr { private void doBackupNow() { String pkg; boolean backupAll = false; + boolean nonIncrementalBackup = false; ArrayList allPkgs = new ArrayList(); while ((pkg = nextArg()) != null) { if (pkg.equals("--all")) { backupAll = true; + } else if (pkg.equals("--non-incremental")) { + nonIncrementalBackup = true; + } else if (pkg.equals("--incremental")) { + nonIncrementalBackup = false; } else { allPkgs.add(pkg); } } if (backupAll) { if (allPkgs.size() == 0) { - System.out.println("Running backup for all packages."); - backupNowAllPackages(); + System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") + + "incremental backup for all packages."); + backupNowAllPackages(nonIncrementalBackup); } else { System.err.println("Provide only '--all' flag or list of packages."); } } else if (allPkgs.size() > 0) { - System.out.println("Running backup for " + allPkgs.size() +" requested packages."); - backupNowPackages(allPkgs); + System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") + + "incremental backup for " + allPkgs.size() +" requested packages."); + backupNowPackages(allPkgs, nonIncrementalBackup); } else { System.err.println("Provide '--all' flag or list of packages."); } diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 80bc1364fa587..540683deeecb2 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -136,6 +136,27 @@ public class BackupManager { */ public static final String EXTRA_BACKUP_SERVICES_AVAILABLE = "backup_services_available"; + /** + * If this flag is passed to {@link #requestBackup(String[], BackupObserver, int)}, + * BackupManager will pass a blank old state to BackupAgents of requested packages. + * + * @hide + */ + @SystemApi + public static final int FLAG_NON_INCREMENTAL_BACKUP = 1; + + /** + * Use with {@link #requestBackup} to force backup of + * package meta data. Typically you do not need to explicitly request this be backed up as it is + * handled internally by the BackupManager. If you are requesting backups with + * FLAG_NON_INCREMENTAL, this package won't automatically be backed up and you have to + * explicitly request for its backup. + * + * @hide + */ + @SystemApi + public static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; + private Context mContext; private static IBackupManager sService; @@ -484,13 +505,34 @@ public class BackupManager { */ @SystemApi public int requestBackup(String[] packages, BackupObserver observer) { + return requestBackup(packages, observer, 0); + } + + /** + * Request an immediate backup, providing an observer to which results of the backup operation + * will be published. The Android backup system will decide for each package whether it will + * be full app data backup or key/value-pair-based backup. + * + *

If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all + * provided packages using the remote transport. + * + * @param packages List of package names to backup. + * @param observer The {@link BackupObserver} to receive callbacks during the backup + * operation. Could be {@code null}. + * @param flags {@link #FLAG_NON_INCREMENTAL_BACKUP}. + * @return {@link BackupManager#SUCCESS} on success; nonzero on error. + * @throws IllegalArgumentException on null or empty {@code packages} param. + * @hide + */ + @SystemApi + public int requestBackup(String[] packages, BackupObserver observer, int flags) { checkServiceBinder(); if (sService != null) { try { BackupObserverWrapper observerWrapper = observer == null ? null : new BackupObserverWrapper(mContext, observer); - return sService.requestBackup(packages, observerWrapper); + return sService.requestBackup(packages, observerWrapper, flags); } catch (RemoteException e) { Log.e(TAG, "requestBackup() couldn't connect"); } diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index a320bbf9d178e..fe23c288c7ce8 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -357,7 +357,9 @@ interface IBackupManager { * @param observer The {@link BackupObserver} to receive callbacks during the backup * operation. * + * @param flags {@link BackupManager#FLAG_NON_INCREMENTAL_BACKUP}. + * * @return Zero on success; nonzero on error. */ - int requestBackup(in String[] packages, IBackupObserver observer); + int requestBackup(in String[] packages, IBackupObserver observer, int flags); } diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 31ecb75c62603..7e825860ead35 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -577,15 +577,18 @@ public class BackupManagerService { public ArrayList fullPackages; public IBackupObserver observer; public boolean userInitiated; + public boolean nonIncrementalBackup; BackupParams(IBackupTransport transport, String dirName, ArrayList kvPackages, - ArrayList fullPackages, IBackupObserver observer, boolean userInitiated) { + ArrayList fullPackages, IBackupObserver observer, boolean userInitiated, + boolean nonIncrementalBackup) { this.transport = transport; this.dirName = dirName; this.kvPackages = kvPackages; this.fullPackages = fullPackages; this.observer = observer; this.userInitiated = userInitiated; + this.nonIncrementalBackup = nonIncrementalBackup; } } @@ -794,7 +797,7 @@ public class BackupManagerService { try { String dirName = transport.transportDirName(); PerformBackupTask pbt = new PerformBackupTask(transport, dirName, - queue, oldJournal, null, null, false); + queue, oldJournal, null, null, false, false /* nonIncremental */); Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt); sendMessage(pbtMessage); } catch (Exception e) { @@ -1033,7 +1036,8 @@ public class BackupManagerService { mWakelock.acquire(); PerformBackupTask pbt = new PerformBackupTask(params.transport, params.dirName, - kvQueue, null, params.observer, params.fullPackages, true); + kvQueue, null, params.observer, params.fullPackages, true, + params.nonIncrementalBackup); Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt); sendMessage(pbtMessage); break; @@ -2492,7 +2496,7 @@ public class BackupManagerService { return token; } - public int requestBackup(String[] packages, IBackupObserver observer) { + public int requestBackup(String[] packages, IBackupObserver observer, int flags) { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup"); if (packages == null || packages.length < 1) { @@ -2510,6 +2514,10 @@ public class BackupManagerService { ArrayList fullBackupList = new ArrayList<>(); ArrayList kvBackupList = new ArrayList<>(); for (String packageName : packages) { + if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) { + kvBackupList.add(packageName); + continue; + } try { PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); @@ -2543,9 +2551,12 @@ public class BackupManagerService { sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED); return BackupManager.ERROR_TRANSPORT_ABORTED; } + + boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0; + Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP); msg.obj = new BackupParams(transport, dirName, kvBackupList, fullBackupList, observer, - true); + true, nonIncrementalBackup); mBackupHandler.sendMessage(msg); return BackupManager.SUCCESS; } @@ -2673,17 +2684,20 @@ public class BackupManagerService { ParcelFileDescriptor mNewState; int mStatus; boolean mFinished; - boolean mUserInitiated; + final boolean mUserInitiated; + final boolean mNonIncremental; public PerformBackupTask(IBackupTransport transport, String dirName, ArrayList queue, File journal, IBackupObserver observer, - ArrayList pendingFullBackups, boolean userInitiated) { + ArrayList pendingFullBackups, boolean userInitiated, + boolean nonIncremental) { mTransport = transport; mOriginalQueue = queue; mJournal = journal; mObserver = observer; mPendingFullBackups = pendingFullBackups; mUserInitiated = userInitiated; + mNonIncremental = nonIncremental; mStateDir = new File(mBaseStateDir, dirName); @@ -2748,6 +2762,10 @@ public class BackupManagerService { // the way. mQueue = (ArrayList) mOriginalQueue.clone(); + // When the transport is forcing non-incremental key/value payloads, we send the + // metadata only if it explicitly asks for it. + boolean skipPm = mNonIncremental; + // The app metadata pseudopackage might also be represented in the // backup queue if apps have been added/removed since the last time // we performed a backup. Drop it from the working queue now that @@ -2758,6 +2776,7 @@ public class BackupManagerService { Slog.i(TAG, "Metadata in queue; eliding"); } mQueue.remove(i); + skipPm = false; break; } } @@ -2785,22 +2804,27 @@ public class BackupManagerService { } } - // The package manager doesn't have a proper etc, but since - // it's running here in the system process we can just set up its agent - // directly and use a synthetic BackupRequest. We always run this pass - // because it's cheap and this way we guarantee that we don't get out of - // step even if we're selecting among various transports at run time. - if (mStatus == BackupTransport.TRANSPORT_OK) { - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( - mPackageManager); - mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL, - IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); - addBackupTrace("PMBA invoke: " + mStatus); + if (skipPm) { + Slog.d(TAG, "Skipping backup of package metadata."); + executeNextState(BackupState.RUNNING_QUEUE); + } else { + // The package manager doesn't have a proper etc, but since + // it's running here in the system process we can just set up its agent + // directly and use a synthetic BackupRequest. We always run this pass + // because it's cheap and this way we guarantee that we don't get out of + // step even if we're selecting among various transports at run time. + if (mStatus == BackupTransport.TRANSPORT_OK) { + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager); + mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL, + IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); + addBackupTrace("PMBA invoke: " + mStatus); - // Because the PMBA is a local instance, it has already executed its - // backup callback and returned. Blow away the lingering (spurious) - // pending timeout message for it. - mBackupHandler.removeMessages(MSG_TIMEOUT); + // Because the PMBA is a local instance, it has already executed its + // backup callback and returned. Blow away the lingering (spurious) + // pending timeout message for it. + mBackupHandler.removeMessages(MSG_TIMEOUT); + } } if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { @@ -3066,6 +3090,7 @@ public class BackupManagerService { if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName); addBackupTrace("invoking " + packageName); + File blankStateName = new File(mStateDir, "blank_state"); mSavedStateName = new File(mStateDir, packageName); mBackupDataName = new File(mDataDir, packageName + ".data"); mNewStateName = new File(mStateDir, packageName + ".new"); @@ -3088,9 +3113,10 @@ public class BackupManagerService { } // In a full backup, we pass a null ParcelFileDescriptor as - // the saved-state "file". This is by definition an incremental, - // so we build a saved state file to pass. - mSavedState = ParcelFileDescriptor.open(mSavedStateName, + // the saved-state "file". For key/value backups we pass the old state if + // an incremental backup is required, and a blank state otherwise. + mSavedState = ParcelFileDescriptor.open( + mNonIncremental ? blankStateName : mSavedStateName, ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary @@ -3120,6 +3146,10 @@ public class BackupManagerService { e.toString()); agentErrorCleanup(); return BackupTransport.AGENT_ERROR; + } finally { + if (mNonIncremental) { + blankStateName.delete(); + } } // At this point the agent is off and running. The next thing to happen will diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 312b878b284c9..d677f5ee04a8d 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -338,9 +338,10 @@ public class Trampoline extends IBackupManager.Stub { } @Override - public int requestBackup(String[] packages, IBackupObserver observer) throws RemoteException { + public int requestBackup(String[] packages, IBackupObserver observer, int flags) + throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.requestBackup(packages, observer) : null; + return (svc != null) ? svc.requestBackup(packages, observer, flags) : null; } @Override