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

Commit e23ad63a authored by Android (Google) Code Review's avatar Android (Google) Code Review Committed by The Android Open Source Project
Browse files

am 67669c9c: Merge change 5196 into donut

Merge commit '67669c9c'

* commit '67669c9c':
  Modify the IBackupTransport API to support bulk restore operations.
parents 69866cea 67669c9c
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -43,14 +43,14 @@ public class RestoreSet implements Parcelable {
     * transport.  This is guaranteed to be valid for the duration of a restore
     * session, but is meaningless once the session has ended.
     */
    public int token;
    public long token;


    public RestoreSet() {
        // Leave everything zero / null
    }

    public RestoreSet(String _name, String _dev, int _token) {
    public RestoreSet(String _name, String _dev, long _token) {
        name = _name;
        device = _dev;
        token = _token;
@@ -65,7 +65,7 @@ public class RestoreSet implements Parcelable {
    public void writeToParcel(Parcel out, int flags) {
        out.writeString(name);
        out.writeString(device);
        out.writeInt(token);
        out.writeLong(token);
    }

    public static final Parcelable.Creator<RestoreSet> CREATOR
@@ -82,6 +82,6 @@ public class RestoreSet implements Parcelable {
    private RestoreSet(Parcel in) {
        name = in.readString();
        device = in.readString();
        token = in.readInt();
        token = in.readLong();
    }
}
+42 −35
Original line number Diff line number Diff line
@@ -54,63 +54,70 @@ interface IBackupTransport {
    long requestBackupTime();

    /**
     * Establish a connection to the back-end data repository, if necessary.  If the transport
     * needs to initialize state that is not tied to individual applications' backup operations,
     * this is where it should be done.
     *
     * @return Zero on success; a nonzero error code on failure.
     */
    int startSession();

    /**
     * Send one application's data to the backup destination.
     * Send one application's data to the backup destination.  The transport may send
     * the data immediately, or may buffer it.  After this is called, {@link #finishBackup}
     * must be called to ensure the data is sent and recorded successfully.
     *
     * @param packageInfo The identity of the application whose data is being backed up.
     *   This specifically includes the signature list for the package.
     * @param data The data stream that resulted from invoking the application's
     *   BackupService.doBackup() method.  This may be a pipe rather than a file on
     *   persistent media, so it may not be seekable.
     * @return Zero on success; a nonzero error code on failure.
     * @return false if errors occurred (the backup should be aborted and rescheduled),
     *   true if everything is OK so far (but {@link #finishBackup} must be called).
     */
    int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor data);
    boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);

    /**
     * Finish sending application data to the backup destination.  This must be
     * called after {@link #performBackup} to ensure that all data is sent.  Only
     * when this method returns true can the backup be assumed to have succeeded.
     *
     * @return false if errors occurred (the backup should be aborted and rescheduled),
     *   true if everything is OK so far (but {@link #finishBackup} must be called).
     */
    boolean finishBackup();

    /**
     * Get the set of backups currently available over this transport.
     *
     * @return Descriptions of the set of restore images available for this device.
     * @return Descriptions of the set of restore images available for this device,
     *   or null if an error occurred (the attempt should be rescheduled).
     **/
    RestoreSet[] getAvailableRestoreSets();

    /**
     * Get the set of applications from a given restore image.
     * Start restoring application data from backup.  After calling this function,
     * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
     * to walk through the actual application data.
     *
     * @param token A backup token as returned by {@link #getAvailableRestoreSets}.
     * @return An array of PackageInfo objects describing all of the applications
     *   available for restore from this restore image.  This should include the list
     *   of signatures for each package so that the Backup Manager can filter using that
     *   information.
     * @param packages List of applications to restore (if data is available).
     *   Application data will be restored in the order given.
     * @return false if errors occurred (the restore should be aborted and rescheduled),
     *   true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
     */
    PackageInfo[] getAppSet(int token);
    boolean startRestore(long token, in PackageInfo[] packages);

    /**
     * Retrieve one application's data from the backing store.
     *
     * @param token The backup record from which a restore is being requested.
     * @param packageInfo The identity of the application whose data is being restored.
     *   This must include the signature list for the package; it is up to the transport
     *   to verify that the requested app's signatures match the saved backup record
     *   because the transport cannot necessarily trust the client device.
     * @param data An open, writable file into which the backup image should be stored.
     * @return Zero on success; a nonzero error code on failure.
     * Get the package name of the next application with data in the backup store.
     * @return The name of one of the packages supplied to {@link #startRestore},
     *   or "" (the empty string) if no more backup data is available,
     *   or null if an error occurred (the restore should be aborted and rescheduled).
     */
    int getRestoreData(int token, in PackageInfo packageInfo, in ParcelFileDescriptor data);
    String nextRestorePackage();

    /**
     * Terminate the backup session, closing files, freeing memory, and cleaning up whatever
     * other state the transport required.
     *
     * @return Zero on success; a nonzero error code on failure.  Even on failure, the session
     *         is torn down and must be restarted if another backup is attempted.
     * Get the data for the application returned by {@link #nextRestorePackage}.
     * @param data An open, writable file into which the backup data should be stored.
     * @return false if errors occurred (the restore should be aborted and rescheduled),
     *   true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
     */
    boolean getRestoreData(in ParcelFileDescriptor outFd);

    /**
     * End a restore session (aborting any in-process data transfer as necessary),
     * freeing any resources and connections used during the restore process.
     */
    int endSession();
    void finishRestore();
}
+58 −77
Original line number Diff line number Diff line
@@ -33,11 +33,8 @@ public class LocalTransport extends IBackupTransport.Stub {
    private Context mContext;
    private PackageManager mPackageManager;
    private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
    private FileFilter mDirFileFilter = new FileFilter() {
        public boolean accept(File f) {
            return f.isDirectory();
        }
    };
    private PackageInfo[] mRestorePackages = null;
    private int mRestorePackage = -1;  // Index into mRestorePackages


    public LocalTransport(Context context) {
@@ -51,21 +48,9 @@ public class LocalTransport extends IBackupTransport.Stub {
        return 0;
    }

    public int startSession() throws RemoteException {
        if (DEBUG) Log.v(TAG, "session started");
        mDataDir.mkdirs();
        return 0;
    }

    public int endSession() throws RemoteException {
        if (DEBUG) Log.v(TAG, "session ended");
        return 0;
    }

    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
    public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
            throws RemoteException {
        if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
        int err = 0;

        File packageDir = new File(mDataDir, packageInfo.packageName);
        packageDir.mkdirs();
@@ -101,9 +86,8 @@ public class LocalTransport extends IBackupTransport.Stub {
                    try {
                        entity.write(buf, 0, dataSize);
                    } catch (IOException e) {
                        Log.e(TAG, "Unable to update key file "
                                + entityFile.getAbsolutePath());
                        err = -1;
                        Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
                        return false;
                    } finally {
                        entity.close();
                    }
@@ -111,14 +95,17 @@ public class LocalTransport extends IBackupTransport.Stub {
                    entityFile.delete();
                }
            }
            return true;
        } catch (IOException e) {
            // oops, something went wrong.  abort the operation and return error.
            Log.v(TAG, "Exception reading backup input:");
            e.printStackTrace();
            err = -1;
            Log.v(TAG, "Exception reading backup input:", e);
            return false;
        }
    }

        return err;
    public boolean finishBackup() throws RemoteException {
        if (DEBUG) Log.v(TAG, "finishBackup()");
        return true;
    }

    // Restore handling
@@ -129,72 +116,66 @@ public class LocalTransport extends IBackupTransport.Stub {
        return array;
    }

    public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
        if (DEBUG) Log.v(TAG, "getting app set " + token);
        // the available packages are the extant subdirs of mDatadir
        File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
        ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
        for (File dir : packageDirs) {
            try {
                PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
                        PackageManager.GET_SIGNATURES);
                if (pkg != null) {
                    packages.add(pkg);
                }
            } catch (NameNotFoundException e) {
                // restore set contains data for a package not installed on the
                // phone -- just ignore it.
            }
    public boolean startRestore(long token, PackageInfo[] packages) {
        if (DEBUG) Log.v(TAG, "start restore " + token);
        mRestorePackages = packages;
        mRestorePackage = -1;
        return true;
    }

        if (DEBUG) {
            Log.v(TAG, "Built app set of " + packages.size() + " entries:");
            for (PackageInfo p : packages) {
                Log.v(TAG, "    + " + p.packageName);
    public String nextRestorePackage() {
        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
        while (++mRestorePackage < mRestorePackages.length) {
            String name = mRestorePackages[mRestorePackage].packageName;
            if (new File(mDataDir, name).isDirectory()) {
                if (DEBUG) Log.v(TAG, "  nextRestorePackage() = " + name);
                return name;
            }
        }

        PackageInfo[] result = new PackageInfo[packages.size()];
        return packages.toArray(result);
        if (DEBUG) Log.v(TAG, "  no more packages to restore");
        return "";
    }

    public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
            throws android.os.RemoteException {
        if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
        // we only support one hardcoded restore set
        if (token != 0) return -1;

        // the data for a given package is at a known location
        File packageDir = new File(mDataDir, packageInfo.packageName);
    public boolean getRestoreData(ParcelFileDescriptor outFd) {
        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
        File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);

        // The restore set is the concatenation of the individual record blobs,
        // each of which is a file in the package's directory
        File[] blobs = packageDir.listFiles();
        if (DEBUG) Log.v(TAG, "   found " + blobs.length + " key files");
        int err = 0;
        if (blobs != null && blobs.length > 0) {
        if (blobs == null) {
            Log.e(TAG, "Error listing directory: " + packageDir);
            return false;  // nextRestorePackage() ensures the dir exists, so this is an error
        }

        // We expect at least some data if the directory exists in the first place
        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.length + " key files");
        BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
        try {
            for (File f : blobs) {
                    copyToRestoreData(f, out);
                }
            } catch (Exception e) {
                Log.e(TAG, "Unable to read backup records");
                err = -1;
            }
        }
        return err;
    }

    private void copyToRestoreData(File f, BackupDataOutput out) throws IOException {
                FileInputStream in = new FileInputStream(f);
                try {
                    int size = (int) f.length();
                    byte[] buf = new byte[size];
                    in.read(buf);
                    String key = new String(Base64.decode(f.getName()));
        if (DEBUG) Log.v(TAG, "   ... copy to stream: key=" + key
                + " size=" + size);
                    if (DEBUG) Log.v(TAG, "    ... key=" + key + " size=" + size);
                    out.writeEntityHeader(key, size);
                    out.writeEntityData(buf, size);
                } finally {
                    in.close();
                }
            }
            return true;
        } catch (IOException e) {
            Log.e(TAG, "Unable to read backup records", e);
            return false;
        }
    }

    public void finishRestore() {
        if (DEBUG) Log.v(TAG, "finishRestore()");
    }
}
+172 −205

File changed.

Preview size limit exceeded, changes collapsed.

+9 −9
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
    // is stored using the package name as a key)
    private static final String GLOBAL_METADATA_KEY = "@meta@";

    private List<ApplicationInfo> mAllApps;
    private List<PackageInfo> mAllPackages;
    private PackageManager mPackageManager;
    private HashMap<String, Metadata> mRestoredSignatures;

@@ -73,9 +73,9 @@ public class PackageManagerBackupAgent extends BackupAgent {

    // We're constructed with the set of applications that are participating
    // in backup.  This set changes as apps are installed & removed.
    PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
    PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
        mPackageManager = packageMgr;
        mAllApps = apps;
        mAllPackages = packages;
        mRestoredSignatures = null;
    }

@@ -118,8 +118,8 @@ public class PackageManagerBackupAgent extends BackupAgent {

            // For each app we have on device, see if we've backed it up yet.  If not,
            // write its signature block to the output, keyed on the package name.
            for (ApplicationInfo app : mAllApps) {
                String packName = app.packageName;
            for (PackageInfo pkg : mAllPackages) {
                String packName = pkg.packageName;
                if (!existing.contains(packName)) {
                    // We haven't stored this app's signatures yet, so we do that now
                    try {
@@ -186,7 +186,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
        }

        // Finally, write the new state blob -- just the list of all apps we handled
        writeStateFile(mAllApps, newState);
        writeStateFile(mAllPackages, newState);
    }

    // "Restore" here is a misnomer.  What we're really doing is reading back the
@@ -327,7 +327,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
    }

    // Util: write out our new backup state file
    private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
    private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
        FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
        DataOutputStream out = new DataOutputStream(outstream);

@@ -338,8 +338,8 @@ public class PackageManagerBackupAgent extends BackupAgent {
            out.write(metaNameBuf);

            // now write all the app names too
            for (ApplicationInfo app : apps) {
                byte[] pkgNameBuf = app.packageName.getBytes();
            for (PackageInfo pkg : pkgs) {
                byte[] pkgNameBuf = pkg.packageName.getBytes();
                out.writeInt(pkgNameBuf.length);
                out.write(pkgNameBuf);
            }