Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd8c13fc authored by Sergey Poromov's avatar Sergey Poromov
Browse files

Synchronize results from runner thread to main full backup thread.

Previously, all results from runner thread - both for preflight
or full backup pass were ignored.
This change adds two synchronized method to get preflight result
and result of full backup pass.
This leads to better coverage of AGENT_ERROR to return in callback
as a result of backup operation.
On the other side we won't start backup pass in main thread
if preflight check hasn't been succeeded.

Change-Id: Id5f9e4c956a1bd5c396d59b7ad2098139a15e69d
parent 209fd91d
Loading
Loading
Loading
Loading
+191 −130
Original line number Diff line number Diff line
@@ -3503,12 +3503,12 @@ public class BackupManagerService {
    class FullBackupEngine {
        OutputStream mOutput;
        FullBackupPreflight mPreflightHook;
        IFullBackupRestoreObserver mObserver;
        IBackupAgent mAgent;
        File mFilesDir;
        File mManifestFile;
        File mMetadataFile;
        boolean mIncludeApks;
        PackageInfo mPkg;

        class FullBackupRunner implements Runnable {
            PackageInfo mPackage;
@@ -3577,39 +3577,47 @@ public class BackupManagerService {
            }
        }

        FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook,
        FullBackupEngine(OutputStream output, FullBackupPreflight preflightHook, PackageInfo pkg,
                         boolean alsoApks) {
            mOutput = output;
            mPreflightHook = preflightHook;
            mPkg = pkg;
            mIncludeApks = alsoApks;
            mFilesDir = new File("/data/system");
            mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
        }

        public int backupOnePackage(PackageInfo pkg) throws RemoteException {
            int result = BackupTransport.TRANSPORT_OK;
            Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);

            mAgent = bindToAgentSynchronous(pkg.applicationInfo,
                    IApplicationThread.BACKUP_MODE_FULL);
            if (mAgent != null) {
                ParcelFileDescriptor[] pipes = null;
                try {
                    // Call the preflight hook, if any
                    if (mPreflightHook != null) {
                        result = mPreflightHook.preflightFullBackup(pkg, mAgent);
        public int preflightCheck() throws RemoteException {
            if (mPreflightHook == null) {
                if (MORE_DEBUG) {
                    Slog.v(TAG, "No preflight check");
                }
                return BackupTransport.TRANSPORT_OK;
            }
            if (initializeAgent()) {
                int result = mPreflightHook.preflightFullBackup(mPkg, mAgent);
                if (MORE_DEBUG) {
                    Slog.v(TAG, "preflight returned " + result);
                }
                return result;
            } else {
                Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
                return BackupTransport.AGENT_ERROR;
            }
        }

                    // If we're still good to go after preflighting, start moving data
                    if (result == BackupTransport.TRANSPORT_OK) {
        public int backupOnePackage() throws RemoteException {
            int result = BackupTransport.AGENT_ERROR;

            if (initializeAgent()) {
                ParcelFileDescriptor[] pipes = null;
                try {
                    pipes = ParcelFileDescriptor.createPipe();

                        ApplicationInfo app = pkg.applicationInfo;
                        final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
                    ApplicationInfo app = mPkg.applicationInfo;
                    final boolean isSharedStorage =
                            mPkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
                    final boolean sendApk = mIncludeApks
                            && !isSharedStorage
                            && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
@@ -3617,11 +3625,11 @@ public class BackupManagerService {
                            (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);

                    // TODO: http://b/22388012
                        byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(mPkg.packageName,
                            UserHandle.USER_SYSTEM);

                    final int token = generateToken();
                        FullBackupRunner runner = new FullBackupRunner(pkg, mAgent, pipes[1],
                    FullBackupRunner runner = new FullBackupRunner(mPkg, mAgent, pipes[1],
                            token, sendApk, !isSharedStorage, widgetBlob);
                    pipes[1].close();   // the runner has dup'd it
                    pipes[1] = null;
@@ -3629,24 +3637,18 @@ public class BackupManagerService {
                    t.start();

                    // Now pull data from the app and stuff it into the output
                        try {
                    routeSocketDataToOutput(pipes[0], mOutput);
                        } catch (IOException e) {
                            Slog.i(TAG, "Caught exception reading from agent", e);
                            result = BackupTransport.AGENT_ERROR;
                        }

                    if (!waitUntilOperationComplete(token)) {
                            Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
                            result = BackupTransport.AGENT_ERROR;
                        Slog.e(TAG, "Full backup failed on package " + mPkg.packageName);
                    } else {
                        if (MORE_DEBUG) {
                                Slog.d(TAG, "Full package backup success: " + pkg.packageName);
                            }
                            Slog.d(TAG, "Full package backup success: " + mPkg.packageName);
                        }
                        result = BackupTransport.TRANSPORT_OK;
                    }
                } catch (IOException e) {
                    Slog.e(TAG, "Error backing up " + pkg.packageName, e);
                    Slog.e(TAG, "Error backing up " + mPkg.packageName, e);
                    result = BackupTransport.AGENT_ERROR;
                } finally {
                    try {
@@ -3662,15 +3664,14 @@ public class BackupManagerService {
                    }
                }
            } else {
                Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
                result = BackupTransport.AGENT_ERROR;
                Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
            }
            tearDown(pkg);
            tearDown();
            return result;
        }

        public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
            if (mAgent != null) {
            if (initializeAgent()) {
                try {
                    mAgent.doQuotaExceeded(backupDataBytes, quotaBytes);
                } catch (RemoteException e) {
@@ -3679,6 +3680,17 @@ public class BackupManagerService {
            }
        }

        private boolean initializeAgent() {
            if (mAgent == null) {
                if (MORE_DEBUG) {
                    Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
                }
                mAgent = bindToAgentSynchronous(mPkg.applicationInfo,
                        IApplicationThread.BACKUP_MODE_FULL);
            }
            return mAgent != null;
        }

        private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
            // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
            // TODO: handle backing up split APKs
@@ -3795,9 +3807,9 @@ public class BackupManagerService {
            destination.setLastModified(0);
        }

        private void tearDown(PackageInfo pkg) {
            if (pkg != null) {
                final ApplicationInfo app = pkg.applicationInfo;
        private void tearDown() {
            if (mPkg != null) {
                final ApplicationInfo app = mPkg.applicationInfo;
                if (app != null) {
                    tearDownAgentAndKill(app);
                }
@@ -4166,9 +4178,10 @@ public class BackupManagerService {
                    final boolean isSharedStorage =
                            pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);

                    mBackupEngine = new FullBackupEngine(out, pkg.packageName, null, mIncludeApks);
                    mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks);
                    sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
                    mBackupEngine.backupOnePackage(pkg);
                    // Don't need to check preflight result as there is no preflight hook.
                    mBackupEngine.backupOnePackage();

                    // after the app's agent runs to handle its private filesystem
                    // contents, back up any OBB content it has on its behalf.
@@ -4314,12 +4327,11 @@ public class BackupManagerService {
                final byte[] buffer = new byte[8192];
                for (int i = 0; i < N; i++) {
                    PackageInfo currentPackage = mPackages.get(i);
                    String packageName = currentPackage.packageName;
                    if (DEBUG) {
                        Slog.i(TAG, "Initiating full-data transport backup of "
                                + currentPackage.packageName);
                        Slog.i(TAG, "Initiating full-data transport backup of " + packageName);
                    }
                    EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE,
                            currentPackage.packageName);
                    EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName);

                    transportPipes = ParcelFileDescriptor.createPipe();

@@ -4335,10 +4347,9 @@ public class BackupManagerService {

                        // Now set up the backup engine / data source end of things
                        enginePipes = ParcelFileDescriptor.createPipe();
                        CountDownLatch runnerLatch = new CountDownLatch(1);
                        SinglePackageBackupRunner backupRunner =
                                new SinglePackageBackupRunner(enginePipes[1], currentPackage,
                                        transport, runnerLatch);
                                        transport);
                        // The runner dup'd the pipe half, so we close it here
                        enginePipes[1].close();
                        enginePipes[1] = null;
@@ -4354,12 +4365,16 @@ public class BackupManagerService {
                        FileOutputStream out = new FileOutputStream(
                                transportPipes[1].getFileDescriptor());
                        long totalRead = 0;
                        final long expectedSize = backupRunner.expectedSize();
                        if (expectedSize < 0) {
                            backupPackageStatus = BackupTransport.AGENT_ERROR;
                            sendBackupOnPackageResult(mBackupObserver, currentPackage.packageName,
                                    BackupManager.ERROR_AGENT_FAILURE);
                        final long preflightResult = backupRunner.getPreflightResultBlocking();
                        // Preflight result is negative if some error happened on preflight.
                        if (preflightResult < 0) {
                            if (MORE_DEBUG) {
                                Slog.d(TAG, "Backup error after preflight of package "
                                        + packageName + ": " + preflightResult
                                        + ", not running backup.");
                            }
                            backupPackageStatus = (int) preflightResult;
                        } else {
                            int nRead = 0;
                            do {
                                if (!mKeepRunning.get()) {
@@ -4376,21 +4391,23 @@ public class BackupManagerService {
                                    out.write(buffer, 0, nRead);
                                    backupPackageStatus = transport.sendBackupData(nRead);
                                    totalRead += nRead;
                                if (mBackupObserver != null && expectedSize > 0) {
                                    sendBackupOnUpdate(mBackupObserver, currentPackage.packageName,
                                        new BackupProgress(expectedSize, totalRead));
                                    if (mBackupObserver != null && preflightResult > 0) {
                                        sendBackupOnUpdate(mBackupObserver, packageName,
                                                new BackupProgress(preflightResult, totalRead));
                                    }
                                }
                        } while (nRead > 0 && backupPackageStatus == BackupTransport.TRANSPORT_OK);
                            } while (nRead > 0
                                    && backupPackageStatus == BackupTransport.TRANSPORT_OK);

                            // Despite preflight succeeded, package still can hit quota on flight.
                            if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
                            long quota = transport.getBackupQuota(currentPackage.packageName, true);
                            if (MORE_DEBUG) {
                                Slog.d(TAG, "Package hit quota limit " + currentPackage.packageName
                                long quota = transport.getBackupQuota(packageName, true);
                                Slog.w(TAG, "Package hit quota limit in-flight " + packageName
                                        + ": " + totalRead + " of " + quota);
                            }
                                backupRunner.sendQuotaExceeded(totalRead, quota);
                            }
                        }


                        // If we've lost our running criteria, tell the transport to cancel
                        // and roll back this (partial) backup payload; otherwise tell it
@@ -4409,14 +4426,23 @@ public class BackupManagerService {
                            }
                        }

                        // We still could fail in backup runner thread, getting result from there.
                        int backupRunnerResult = backupRunner.getBackupResultBlocking();
                        if (backupPackageStatus != BackupTransport.TRANSPORT_ERROR
                                && backupRunnerResult != BackupTransport.TRANSPORT_OK) {
                            // If there was an error in runner thread and
                            // not TRANSPORT_ERROR here, overwrite it.
                            backupPackageStatus = backupRunnerResult;
                        }

                        if (MORE_DEBUG) {
                            Slog.i(TAG, "Done trying to send backup data: result="
                                    + backupPackageStatus);
                        }

                        if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
                            Slog.e(TAG, "Error " + backupPackageStatus
                                    + " backing up " + currentPackage.packageName);
                            Slog.e(TAG, "Error " + backupPackageStatus + " backing up "
                                    + packageName);
                        }

                        // Also ask the transport how long it wants us to wait before
@@ -4431,33 +4457,36 @@ public class BackupManagerService {
                    // Roll this package to the end of the backup queue if we're
                    // in a queue-driven mode (regardless of success/failure)
                    if (mUpdateSchedule) {
                        enqueueFullBackup(currentPackage.packageName,
                                System.currentTimeMillis());
                        enqueueFullBackup(packageName, System.currentTimeMillis());
                    }

                    if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
                        sendBackupOnPackageResult(mBackupObserver, currentPackage.packageName,
                        sendBackupOnPackageResult(mBackupObserver, packageName,
                                BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
                        if (DEBUG) {
                            Slog.i(TAG, "Transport rejected backup of "
                                    + currentPackage.packageName
                            Slog.i(TAG, "Transport rejected backup of " + packageName
                                    + ", skipping");
                        }
                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE,
                                currentPackage.packageName, "transport rejected");
                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
                                "transport rejected");
                        // Do nothing, clean up, and continue looping.
                    } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
                        sendBackupOnPackageResult(mBackupObserver, currentPackage.packageName,
                        sendBackupOnPackageResult(mBackupObserver, packageName,
                                BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
                        if (DEBUG) {
                            Slog.i(TAG, "Transport quota exceeded for package: "
                                    + currentPackage.packageName);
                        }
                            Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
                            EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
                                currentPackage.packageName);
                                    packageName);
                        }
                        // Do nothing, clean up, and continue looping.
                    } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
                        sendBackupOnPackageResult(mBackupObserver, packageName,
                                BackupManager.ERROR_AGENT_FAILURE);
                        Slog.w(TAG, "Application failure for package: " + packageName);
                        EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
                        // Do nothing, clean up, and continue looping.
                    } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
                        sendBackupOnPackageResult(mBackupObserver, currentPackage.packageName,
                        sendBackupOnPackageResult(mBackupObserver, packageName,
                            BackupManager.ERROR_TRANSPORT_ABORTED);
                        Slog.w(TAG, "Transport failed; aborting backup: " + backupPackageStatus);
                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
@@ -4466,11 +4495,10 @@ public class BackupManagerService {
                        return;
                    } else {
                        // Success!
                        sendBackupOnPackageResult(mBackupObserver, currentPackage.packageName,
                        sendBackupOnPackageResult(mBackupObserver, packageName,
                                BackupManager.SUCCESS);
                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS,
                                currentPackage.packageName);
                        logBackupComplete(currentPackage.packageName);
                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName);
                        logBackupComplete(packageName);
                    }
                    cleanUpPipes(transportPipes);
                    cleanUpPipes(enginePipes);
@@ -4621,28 +4649,41 @@ public class BackupManagerService {
            final ParcelFileDescriptor mOutput;
            final PackageInfo mTarget;
            final FullBackupPreflight mPreflight;
            final CountDownLatch mLatch;
            final CountDownLatch mPreflightLatch;
            final CountDownLatch mBackupLatch;
            private FullBackupEngine mEngine;
            private volatile int mPreflightResult;
            private volatile int mBackupResult;

            SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
                    IBackupTransport transport, CountDownLatch latch) throws IOException {
                    IBackupTransport transport) throws IOException {
                mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
                mTarget = target;
                mPreflight = new SinglePackageBackupPreflight(transport);
                mLatch = latch;
                mPreflightLatch = new CountDownLatch(1);
                mBackupLatch = new CountDownLatch(1);
                mPreflightResult = BackupTransport.TRANSPORT_OK;
                mBackupResult = BackupTransport.TRANSPORT_OK;
            }

            @Override
            public void run() {
                try {
                FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
                    mEngine = new FullBackupEngine(out, mTarget.packageName,
                            mPreflight, false);
                    mEngine.backupOnePackage(mTarget);
                mEngine = new FullBackupEngine(out, mPreflight, mTarget, false);
                try {
                    try {
                        mPreflightResult = mEngine.preflightCheck();
                    } finally {
                        mPreflightLatch.countDown();
                    }
                    // If there is no error on preflight, continue backup.
                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
                        mBackupResult = mEngine.backupOnePackage();
                    }
                } catch (Exception e) {
                    Slog.e(TAG, "Exception during full package backup of " + mTarget);
                    Slog.e(TAG, "Exception during full package backup of " + mTarget.packageName);
                } finally {
                    mLatch.countDown();
                    mBackupLatch.countDown();
                    try {
                        mOutput.close();
                    } catch (IOException e) {
@@ -4655,8 +4696,28 @@ public class BackupManagerService {
                mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes);
            }

            long expectedSize() {
            // If preflight succeeded, returns positive number - preflight size,
            // otherwise return negative error code.
            long getPreflightResultBlocking() {
                try {
                    mPreflightLatch.await();
                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
                        return mPreflight.getExpectedSizeOrErrorCode();
                    } else {
                        return mPreflightResult;
                    }
                } catch (InterruptedException e) {
                    return BackupTransport.AGENT_ERROR;
                }
            }

            int getBackupResultBlocking() {
                try {
                    mBackupLatch.await();
                    return mBackupResult;
                } catch (InterruptedException e) {
                    return BackupTransport.AGENT_ERROR;
                }
            }
        }
    }