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

Commit bb8d0dfe authored by Christopher Tate's avatar Christopher Tate Committed by The Android Open Source Project
Browse files

am c7b31e3c: The rest of the basic flow for restore

Merge commit 'c7b31e3c'

* commit 'c7b31e3c':
  The rest of the basic flow for restore
parents 4f84953a c7b31e3c
Loading
Loading
Loading
Loading
+203 −94
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -53,6 +54,7 @@ import com.android.internal.backup.IBackupTransport;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.String;
import java.util.ArrayList;
@@ -71,6 +73,9 @@ class BackupManagerService extends IBackupManager.Stub {
    private static final int MSG_RUN_BACKUP = 1;
    private static final int MSG_RUN_FULL_BACKUP = 2;

    // Timeout interval for deciding that a bind or clear-data has taken too long
    static final long TIMEOUT_INTERVAL = 10 * 1000;

    private Context mContext;
    private PackageManager mPackageManager;
    private final IActivityManager mActivityManager;
@@ -108,6 +113,10 @@ class BackupManagerService extends IBackupManager.Stub {
    private IBackupAgent mConnectedAgent;
    private volatile boolean mConnecting;

    // A similar synchronicity mechanism around clearing apps' data for restore
    private final Object mClearDataLock = new Object();
    private volatile boolean mClearingData;

    private int mTransportId;

    private File mStateDir;
@@ -207,80 +216,6 @@ class BackupManagerService extends IBackupManager.Stub {
        }
    }

    void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
        final String packageName = request.appInfo.packageName;
        Log.d(TAG, "processOneBackup doBackup() on " + packageName);

        try {
            // Look up the package info & signatures.  This is first so that if it
            // throws an exception, there's no file setup yet that would need to
            // be unraveled.
            PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
                    PackageManager.GET_SIGNATURES);

            // !!! TODO: get the state file dir from the transport
            File savedStateName = new File(mStateDir, packageName);
            File backupDataName = new File(mDataDir, packageName + ".data");
            File newStateName = new File(mStateDir, packageName + ".new");
            
            // In a full backup, we pass a null ParcelFileDescriptor as
            // the saved-state "file"
            ParcelFileDescriptor savedState = (request.fullBackup) ? null
                    : ParcelFileDescriptor.open(savedStateName,
                        ParcelFileDescriptor.MODE_READ_ONLY |
                        ParcelFileDescriptor.MODE_CREATE);

            backupDataName.delete();
            ParcelFileDescriptor backupData =
                    ParcelFileDescriptor.open(backupDataName,
                            ParcelFileDescriptor.MODE_READ_WRITE |
                            ParcelFileDescriptor.MODE_CREATE);

            newStateName.delete();
            ParcelFileDescriptor newState =
                    ParcelFileDescriptor.open(newStateName,
                            ParcelFileDescriptor.MODE_READ_WRITE |
                            ParcelFileDescriptor.MODE_CREATE);

            // Run the target's backup pass
            boolean success = false;
            try {
                agent.doBackup(savedState, backupData, newState);
                success = true;
            } finally {
                if (savedState != null) {
                    savedState.close();
                }
                backupData.close();
                newState.close();
            }

            // Now propagate the newly-backed-up data to the transport
            if (success) {
                if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
                backupData =
                    ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
                int error = transport.performBackup(packInfo, backupData);

                // !!! TODO: After successful transport, delete the now-stale data
                // and juggle the files so that next time the new state is passed
                //backupDataName.delete();
                newStateName.renameTo(savedStateName);
            }
        } catch (NameNotFoundException e) {
            Log.e(TAG, "Package not found on backup: " + packageName);
        } catch (FileNotFoundException fnf) {
            Log.w(TAG, "File not found on backup: ");
            fnf.printStackTrace();
        } catch (RemoteException e) {
            Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
            e.printStackTrace();
        } catch (Exception e) {
            Log.w(TAG, "Final exception guard in backup: ");
            e.printStackTrace();
        }
    }

    // Add the backup agents in the given package to our set of known backup participants.
    // If 'packageName' is null, adds all backup agents in the whole system.
    void addPackageParticipantsLocked(String packageName) {
@@ -424,11 +359,14 @@ class BackupManagerService extends IBackupManager.Stub {
                    Log.d(TAG, "awaiting agent for " + app);

                    // success; wait for the agent to arrive
                    while (mConnecting && mConnectedAgent == null) {
                    // only wait 10 seconds for the clear data to happen
                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
                    while (mConnecting && mConnectedAgent == null
                            && (System.currentTimeMillis() < timeoutMark)) {
                        try {
                            mAgentConnectLock.wait(10000);
                            mAgentConnectLock.wait(5000);
                        } catch (InterruptedException e) {
                            // just retry
                            // just bail
                            return null;
                        }
                    }
@@ -447,6 +385,37 @@ class BackupManagerService extends IBackupManager.Stub {
        return agent;
    }

    // clear an application's data, blocking until the operation completes or times out
    void clearApplicationDataSynchronous(String packageName) {
        ClearDataObserver observer = new ClearDataObserver();

        synchronized(mClearDataLock) {
            mClearingData = true;
            mPackageManager.clearApplicationUserData(packageName, observer);

            // only wait 10 seconds for the clear data to happen
            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
                try {
                    mClearDataLock.wait(5000);
                } catch (InterruptedException e) {
                    // won't happen, but still.
                    mClearingData = false;
                }
            }
        }
    }

    class ClearDataObserver extends IPackageDataObserver.Stub {
        public void onRemoveCompleted(String packageName, boolean succeeded)
                throws android.os.RemoteException {
            synchronized(mClearDataLock) {
                mClearingData = false;
                notifyAll();
            }
        }
    }

    // ----- Back up a set of applications via a worker thread -----

    class PerformBackupThread extends Thread {
@@ -508,13 +477,88 @@ class BackupManagerService extends IBackupManager.Stub {
                    mActivityManager.unbindBackupAgent(request.appInfo);
                } catch (SecurityException ex) {
                    // Try for the next one.
                    Log.d(TAG, "error in bind", ex);
                    Log.d(TAG, "error in bind/backup", ex);
                } catch (RemoteException e) {
                    // can't happen
                    Log.v(TAG, "bind/backup threw");
                    e.printStackTrace();
                }

            }
        }

        void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
            final String packageName = request.appInfo.packageName;
            Log.d(TAG, "processOneBackup doBackup() on " + packageName);

            try {
                // Look up the package info & signatures.  This is first so that if it
                // throws an exception, there's no file setup yet that would need to
                // be unraveled.
                PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
                        PackageManager.GET_SIGNATURES);

                // !!! TODO: get the state file dir from the transport
                File savedStateName = new File(mStateDir, packageName);
                File backupDataName = new File(mDataDir, packageName + ".data");
                File newStateName = new File(mStateDir, packageName + ".new");

                // In a full backup, we pass a null ParcelFileDescriptor as
                // the saved-state "file"
                ParcelFileDescriptor savedState = (request.fullBackup) ? null
                        : ParcelFileDescriptor.open(savedStateName,
                            ParcelFileDescriptor.MODE_READ_ONLY |
                            ParcelFileDescriptor.MODE_CREATE);

                backupDataName.delete();
                ParcelFileDescriptor backupData =
                        ParcelFileDescriptor.open(backupDataName,
                                ParcelFileDescriptor.MODE_READ_WRITE |
                                ParcelFileDescriptor.MODE_CREATE);

                newStateName.delete();
                ParcelFileDescriptor newState =
                        ParcelFileDescriptor.open(newStateName,
                                ParcelFileDescriptor.MODE_READ_WRITE |
                                ParcelFileDescriptor.MODE_CREATE);

                // Run the target's backup pass
                boolean success = false;
                try {
                    agent.doBackup(savedState, backupData, newState);
                    success = true;
                } finally {
                    if (savedState != null) {
                        savedState.close();
                    }
                    backupData.close();
                    newState.close();
                }

                // Now propagate the newly-backed-up data to the transport
                if (success) {
                    if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
                    backupData =
                        ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
                    int error = transport.performBackup(packInfo, backupData);

                    // !!! TODO: After successful transport, delete the now-stale data
                    // and juggle the files so that next time the new state is passed
                    //backupDataName.delete();
                    newStateName.renameTo(savedStateName);
                }
            } catch (NameNotFoundException e) {
                Log.e(TAG, "Package not found on backup: " + packageName);
            } catch (FileNotFoundException fnf) {
                Log.w(TAG, "File not found on backup: ");
                fnf.printStackTrace();
            } catch (RemoteException e) {
                Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
                e.printStackTrace();
            } catch (Exception e) {
                Log.w(TAG, "Final exception guard in backup: ");
                e.printStackTrace();
            }
        }
    }


@@ -524,12 +568,12 @@ class BackupManagerService extends IBackupManager.Stub {
    // ApplicationInfo struct if it is; null if not.
    //
    // !!! TODO: also consider signatures
    ApplicationInfo isRestorable(PackageInfo packageInfo) {
    PackageInfo isRestorable(PackageInfo packageInfo) {
        if (packageInfo.packageName != null) {
            try {
                ApplicationInfo app = mPackageManager.getApplicationInfo(packageInfo.packageName,
                PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
                        PackageManager.GET_SIGNATURES);
                if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
                if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
                    return app;
                }
            } catch (Exception e) {
@@ -541,6 +585,7 @@ class BackupManagerService extends IBackupManager.Stub {

    class PerformRestoreThread extends Thread {
        private IBackupTransport mTransport;
        private RestoreSet mImage;

        PerformRestoreThread(IBackupTransport transport) {
            mTransport = transport;
@@ -556,6 +601,7 @@ class BackupManagerService extends IBackupManager.Stub {
             * 3. for each app in the restore set:
             *    3.a. if it's restorable on this device, add it to the restore queue
             * 4. for each app in the restore queue:
             *    4.a. clear the app data
             *    4.b. get the restore data for the app from the transport
             *    4.c. launch the backup agent for the app
             *    4.d. agent.doRestore() with the data from the server
@@ -577,13 +623,14 @@ class BackupManagerService extends IBackupManager.Stub {
                    RestoreSet[] images = mTransport.getAvailableRestoreSets();
                    if (images.length > 0) {
                        // !!! for now we always take the first set
                        RestoreSet image = images[0];
                        mImage = images[0];

                        // build the set of apps we will attempt to restore
                        PackageInfo[] packages = mTransport.getAppSet(image.token);
                        HashSet<ApplicationInfo> appsToRestore = new HashSet<ApplicationInfo>();
                        PackageInfo[] packages = mTransport.getAppSet(mImage.token);
                        HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>();
                        for (PackageInfo pkg: packages) {
                            ApplicationInfo app = isRestorable(pkg);
                            // get the real PackageManager idea of the package
                            PackageInfo app = isRestorable(pkg);
                            if (app != null) {
                                appsToRestore.add(app);
                            }
@@ -609,19 +656,23 @@ class BackupManagerService extends IBackupManager.Stub {
        }

        // restore each app in the queue
        void doQueuedRestores(HashSet<ApplicationInfo> appsToRestore) {
            for (ApplicationInfo app : appsToRestore) {
        void doQueuedRestores(HashSet<PackageInfo> appsToRestore) {
            for (PackageInfo app : appsToRestore) {
                Log.d(TAG, "starting agent for restore of " + app);

                IBackupAgent agent = null;
                try {
                    agent = bindToAgentSynchronous(app, IApplicationThread.BACKUP_MODE_RESTORE);
                    // Remove the app's data first
                    clearApplicationDataSynchronous(app.packageName);

                    // Now perform the restore into the clean app
                    IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
                            IApplicationThread.BACKUP_MODE_RESTORE);
                    if (agent != null) {
                        processOneRestore(app, agent);
                    }

                    // unbind even on timeout, just in case
                    mActivityManager.unbindBackupAgent(app);
                    mActivityManager.unbindBackupAgent(app.applicationInfo);
                } catch (SecurityException ex) {
                    // Try for the next one.
                    Log.d(TAG, "error in bind", ex);
@@ -632,9 +683,67 @@ class BackupManagerService extends IBackupManager.Stub {
            }
        }

        // do the guts of a restore
        void processOneRestore(ApplicationInfo app, IBackupAgent agent) {
        // Do the guts of a restore of one application, derived from the 'mImage'
        // restore set via the 'mTransport' transport.
        void processOneRestore(PackageInfo app, IBackupAgent agent) {
            // !!! TODO: actually run the restore through mTransport
            final String packageName = app.packageName;

            // !!! TODO: get the dirs from the transport
            File backupDataName = new File(mDataDir, packageName + ".restore");
            backupDataName.delete();
            try {
                ParcelFileDescriptor backupData =
                    ParcelFileDescriptor.open(backupDataName,
                            ParcelFileDescriptor.MODE_READ_WRITE |
                            ParcelFileDescriptor.MODE_CREATE);

                // Run the transport's restore pass
                // Run the target's backup pass
                int err = -1;
                try {
                    err = mTransport.getRestoreData(mImage.token, app, backupData);
                } catch (RemoteException e) {
                    // can't happen
                } finally {
                    backupData.close();
                }

                // Okay, we have the data.  Now have the agent do the restore.
                File newStateName = new File(mStateDir, packageName + ".new");
                ParcelFileDescriptor newState =
                    ParcelFileDescriptor.open(newStateName,
                            ParcelFileDescriptor.MODE_READ_WRITE |
                            ParcelFileDescriptor.MODE_CREATE);

                backupData = ParcelFileDescriptor.open(backupDataName,
                            ParcelFileDescriptor.MODE_READ_ONLY);

                boolean success = false;
                try {
                    agent.doRestore(backupData, newState);
                    success = true;
                } catch (Exception e) {
                    Log.e(TAG, "Restore failed for " + packageName);
                    e.printStackTrace();
                } finally {
                    newState.close();
                    backupData.close();
                }

                // if everything went okay, remember the recorded state now
                if (success) {
                    File savedStateName = new File(mStateDir, packageName);
                    newStateName.renameTo(savedStateName);
                }
            } catch (FileNotFoundException fnfe) {
                Log.v(TAG, "Couldn't open file for restore: " + fnfe);
            } catch (IOException ioe) {
                Log.e(TAG, "Unable to process restore file: " + ioe);
            } catch (Exception e) {
                Log.e(TAG, "Final exception guard in restore:");
                e.printStackTrace();
            }
        }
    }

+8 −5
Original line number Diff line number Diff line
@@ -10422,6 +10422,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
            // Not backing this app up any more; reset its OOM adjustment
            updateOomAdjLocked(proc);

            // If the app crashed during backup, 'thread' will be null here
            if (proc.thread != null) {
                try {
                    proc.thread.scheduleDestroyBackupAgent(appInfo);
                } catch (Exception e) {
@@ -10430,6 +10432,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
                }
            }
        }
    }
    // =========================================================
    // BROADCASTS
    // =========================================================