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

Commit c9e0cf90 authored by Nikita Ioffe's avatar Nikita Ioffe Committed by Android (Google) Code Review
Browse files

Merge "RMS: Delete user data snapshots when deleting a rollback"

parents c5fe7b19 952aa7b4
Loading
Loading
Loading
Loading
+34 −1
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.pm.VersionedPackage;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.util.IntArray;
import android.util.IntArray;
import android.util.SparseLongArray;


import java.util.ArrayList;
import java.util.ArrayList;


@@ -73,6 +74,18 @@ public final class PackageRollbackInfo implements Parcelable {
     */
     */
    private final boolean mIsApex;
    private final boolean mIsApex;


    /*
     * The list of users the package is installed for.
     */
    // NOTE: Not a part of the Parcelable representation of this object.
    private final IntArray mInstalledUsers;

    /**
     * A mapping between user and an inode of theirs CE data snapshot.
     */
    // NOTE: Not a part of the Parcelable representation of this object.
    private final SparseLongArray mCeSnapshotInodes;

    /**
    /**
     * Returns the name of the package to roll back from.
     * Returns the name of the package to roll back from.
     */
     */
@@ -125,16 +138,34 @@ public final class PackageRollbackInfo implements Parcelable {
        return mIsApex;
        return mIsApex;
    }
    }


    /** @hide */
    public IntArray getInstalledUsers() {
        return mInstalledUsers;
    }

    /** @hide */
    public SparseLongArray getCeSnapshotInodes() {
        return mCeSnapshotInodes;
    }

    /** @hide */
    public void putCeSnapshotInode(int userId, long ceSnapshotInode) {
        mCeSnapshotInodes.put(userId, ceSnapshotInode);
    }

    /** @hide */
    /** @hide */
    public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
    public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
            VersionedPackage packageRolledBackTo,
            VersionedPackage packageRolledBackTo,
            @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
            @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
            boolean isApex) {
            boolean isApex, @NonNull IntArray installedUsers,
            @NonNull SparseLongArray ceSnapshotInodes) {
        this.mVersionRolledBackFrom = packageRolledBackFrom;
        this.mVersionRolledBackFrom = packageRolledBackFrom;
        this.mVersionRolledBackTo = packageRolledBackTo;
        this.mVersionRolledBackTo = packageRolledBackTo;
        this.mPendingBackups = pendingBackups;
        this.mPendingBackups = pendingBackups;
        this.mPendingRestores = pendingRestores;
        this.mPendingRestores = pendingRestores;
        this.mIsApex = isApex;
        this.mIsApex = isApex;
        this.mInstalledUsers = installedUsers;
        this.mCeSnapshotInodes = ceSnapshotInodes;
    }
    }


    private PackageRollbackInfo(Parcel in) {
    private PackageRollbackInfo(Parcel in) {
@@ -143,6 +174,8 @@ public final class PackageRollbackInfo implements Parcelable {
        this.mIsApex = in.readBoolean();
        this.mIsApex = in.readBoolean();
        this.mPendingRestores = null;
        this.mPendingRestores = null;
        this.mPendingBackups = null;
        this.mPendingBackups = null;
        this.mInstalledUsers = null;
        this.mCeSnapshotInodes = null;
    }
    }


    @Override
    @Override
+54 −4
Original line number Original line Diff line number Diff line
@@ -611,18 +611,43 @@ public class Installer extends SystemService {
        }
        }
    }
    }


    public boolean snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags)
    /**
     * Snapshots user data of the given package.
     *
     * @param pkg name of the package to snapshot user data for.
     * @param userId id of the user whose data to snapshot.
     * @param storageFlags flags controlling which data (CE or DE) to snapshot.
     *
     * @return inode of the snapshot of users CE package data, or {@code 0} if a remote calls
     *  shouldn't be continued. See {@link #checkBeforeRemote}.
     *
     * @throws InstallerException if failed to snapshot user data.
     */
    public long snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags)
            throws InstallerException {
            throws InstallerException {
        if (!checkBeforeRemote()) return false;
        if (!checkBeforeRemote()) return 0;


        try {
        try {
            mInstalld.snapshotAppData(null, pkg, userId, storageFlags);
            return mInstalld.snapshotAppData(null, pkg, userId, storageFlags);
            return true;
        } catch (Exception e) {
        } catch (Exception e) {
            throw InstallerException.from(e);
            throw InstallerException.from(e);
        }
        }
    }
    }


    /**
     * Restores user data snapshot of the given package.
     *
     * @param pkg name of the package to restore user data for.
     * @param appId id of the package to restore user data for.
     * @param ceDataInode inode of CE user data folder of this app.
     * @param userId id of the user whose data to restore.
     * @param storageFlags flags controlling which data (CE or DE) to restore.
     *
     * @return {@code true} if user data restore was successful, or {@code false} if a remote call
     *  shouldn't be continued. See {@link #checkBeforeRemote}.
     *
     * @throws InstallerException if failed to restore user data.
     */
    public boolean restoreAppDataSnapshot(String pkg, @AppIdInt  int appId, long ceDataInode,
    public boolean restoreAppDataSnapshot(String pkg, @AppIdInt  int appId, long ceDataInode,
            String seInfo, @UserIdInt int userId, int storageFlags) throws InstallerException {
            String seInfo, @UserIdInt int userId, int storageFlags) throws InstallerException {
        if (!checkBeforeRemote()) return false;
        if (!checkBeforeRemote()) return false;
@@ -636,6 +661,31 @@ public class Installer extends SystemService {
        }
        }
    }
    }


    /**
     * Deletes user data snapshot of the given package.
     *
     * @param pkg name of the package to delete user data snapshot for.
     * @param userId id of the user whose user data snapshot to delete.
     * @param ceSnapshotInode inode of CE user data snapshot.
     * @param storageFlags flags controlling which user data snapshot (CE or DE) to delete.
     *
     * @return {@code true} if user data snapshot was successfully deleted, or {@code false} if a
     *  remote call shouldn't be continued. See {@link #checkBeforeRemote}.
     *
     * @throws InstallerException if failed to delete user data snapshot.
     */
    public boolean destroyAppDataSnapshot(String pkg, @UserIdInt int userId, long ceSnapshotInode,
            int storageFlags) throws InstallerException {
        if (!checkBeforeRemote()) return false;

        try {
            mInstalld.destroyAppDataSnapshot(null, pkg, userId, ceSnapshotInode, storageFlags);
            return true;
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }

    private static void assertValidInstructionSet(String instructionSet)
    private static void assertValidInstructionSet(String instructionSet)
            throws InstallerException {
            throws InstallerException {
        for (String abi : Build.SUPPORTED_ABIS) {
        for (String abi : Build.SUPPORTED_ABIS) {
+65 −9
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.rollback.RollbackInfo;
import android.os.storage.StorageManager;
import android.os.storage.StorageManager;
import android.util.IntArray;
import android.util.IntArray;
import android.util.Log;
import android.util.Log;
import android.util.SparseLongArray;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer;
@@ -51,11 +52,13 @@ public class AppDataRollbackHelper {
     * Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers},
     * Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers},
     * a specified set of users for whom the package is installed.
     * a specified set of users for whom the package is installed.
     *
     *
     * @return a list of users for which the snapshot is pending, usually because data for one or
     * @return a {@link SnapshotAppDataResult}/
     *         more users is still credential locked.
     * @see SnapshotAppDataResult
     */
     */
    public IntArray snapshotAppData(String packageName, int[] installedUsers) {
    public SnapshotAppDataResult snapshotAppData(String packageName, int[] installedUsers) {
        final IntArray pendingBackups = new IntArray();
        final IntArray pendingBackups = new IntArray();
        final SparseLongArray ceSnapshotInodes = new SparseLongArray();

        for (int user : installedUsers) {
        for (int user : installedUsers) {
            final int storageFlags;
            final int storageFlags;
            if (isUserCredentialLocked(user)) {
            if (isUserCredentialLocked(user)) {
@@ -69,14 +72,17 @@ public class AppDataRollbackHelper {
            }
            }


            try {
            try {
                mInstaller.snapshotAppData(packageName, user, storageFlags);
                long ceSnapshotInode = mInstaller.snapshotAppData(packageName, user, storageFlags);
                if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) {
                    ceSnapshotInodes.put(user, ceSnapshotInode);
                }
            } catch (InstallerException ie) {
            } catch (InstallerException ie) {
                Log.e(TAG, "Unable to create app data snapshot for: " + packageName
                Log.e(TAG, "Unable to create app data snapshot for: " + packageName
                        + ", userId: " + user, ie);
                        + ", userId: " + user, ie);
            }
            }
        }
        }


        return pendingBackups;
        return new SnapshotAppDataResult(pendingBackups, ceSnapshotInodes);
    }
    }


    /**
    /**
@@ -137,6 +143,22 @@ public class AppDataRollbackHelper {
        return changedRollbackData;
        return changedRollbackData;
    }
    }


    /**
     * Deletes an app data data snapshot for a specified package {@code packageName} for a
     * given {@code user}.
     */
    public void destroyAppDataSnapshot(String packageName, int user, long ceSnapshotInode) {
        int storageFlags = Installer.FLAG_STORAGE_DE;
        if (ceSnapshotInode > 0) {
            storageFlags |= Installer.FLAG_STORAGE_CE;
        }
        try {
            mInstaller.destroyAppDataSnapshot(packageName, user, ceSnapshotInode, storageFlags);
        } catch (InstallerException ie) {
            Log.e(TAG, "Unable to delete app data snapshot for " + packageName, ie);
        }
    }

    /**
    /**
     * Computes the list of pending backups and restores for {@code userId} given lists of
     * Computes the list of pending backups and restores for {@code userId} given lists of
     * available and recent rollbacks. Packages pending backup for the given user are added
     * available and recent rollbacks. Packages pending backup for the given user are added
@@ -191,16 +213,28 @@ public class AppDataRollbackHelper {
    }
    }


    /**
    /**
     * Commits the list of pending backups and restores for a given {@code userId}.
     * Commits the list of pending backups and restores for a given {@code userId}. For the pending
     * backups updates corresponding {@code changedRollbackData} with a mapping from {@code userId}
     * to a inode of theirs CE user data snapshot.
     */
     */
    public void commitPendingBackupAndRestoreForUser(int userId,
    public void commitPendingBackupAndRestoreForUser(int userId,
            ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores) {
            ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores,
            List<RollbackData> changedRollbackData) {
        if (!pendingBackups.isEmpty()) {
        if (!pendingBackups.isEmpty()) {
            for (String packageName : pendingBackups) {
            for (String packageName : pendingBackups) {
                try {
                try {
                    mInstaller.snapshotAppData(packageName, userId, Installer.FLAG_STORAGE_CE);
                    long ceSnapshotInode = mInstaller.snapshotAppData(packageName, userId,
                            Installer.FLAG_STORAGE_CE);
                    for (RollbackData data : changedRollbackData) {
                        for (PackageRollbackInfo info : data.packages) {
                            if (info.getPackageName().equals(packageName)) {
                                info.putCeSnapshotInode(userId, ceSnapshotInode);
                            }
                        }
                    }
                } catch (InstallerException ie) {
                } catch (InstallerException ie) {
                    Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie);
                    Log.e(TAG, "Unable to create app data snapshot for: " + packageName
                            + ", userId: " + userId, ie);
                }
                }
            }
            }
        }
        }
@@ -233,4 +267,26 @@ public class AppDataRollbackHelper {
        return StorageManager.isFileEncryptedNativeOrEmulated()
        return StorageManager.isFileEncryptedNativeOrEmulated()
                && !StorageManager.isUserKeyUnlocked(userId);
                && !StorageManager.isUserKeyUnlocked(userId);
    }
    }

    /**
     * Encapsulates a result of {@link #snapshotAppData} method.
     */
    public static final class SnapshotAppDataResult {

        /**
         * A list of users for which the snapshot is pending, usually because data for one or more
         * users is still credential locked.
         */
        public final IntArray pendingBackups;

        /**
         * A mapping between user and an inode of theirs CE data snapshot.
         */
        public final SparseLongArray ceSnapshotInodes;

        public SnapshotAppDataResult(IntArray pendingBackups, SparseLongArray ceSnapshotInodes) {
            this.pendingBackups = pendingBackups;
            this.ceSnapshotInodes = ceSnapshotInodes;
        }
    }
}
}
+42 −20
Original line number Original line Diff line number Diff line
@@ -43,6 +43,7 @@ import android.os.Process;
import android.util.IntArray;
import android.util.IntArray;
import android.util.Log;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.SparseBooleanArray;
import android.util.SparseLongArray;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
@@ -110,7 +111,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
    private final HandlerThread mHandlerThread;
    private final HandlerThread mHandlerThread;
    private final Installer mInstaller;
    private final Installer mInstaller;
    private final RollbackPackageHealthObserver mPackageHealthObserver;
    private final RollbackPackageHealthObserver mPackageHealthObserver;
    private final AppDataRollbackHelper mUserdataHelper;
    private final AppDataRollbackHelper mAppDataRollbackHelper;


    RollbackManagerServiceImpl(Context context) {
    RollbackManagerServiceImpl(Context context) {
        mContext = context;
        mContext = context;
@@ -124,7 +125,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
        mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
        mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));


        mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
        mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
        mUserdataHelper = new AppDataRollbackHelper(mInstaller);
        mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);


        // Kick off loading of the rollback data from strorage in a background
        // Kick off loading of the rollback data from strorage in a background
        // thread.
        // thread.
@@ -449,7 +450,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                for (PackageRollbackInfo info : data.packages) {
                for (PackageRollbackInfo info : data.packages) {
                    if (info.getPackageName().equals(packageName)) {
                    if (info.getPackageName().equals(packageName)) {
                        iter.remove();
                        iter.remove();
                        mRollbackStore.deleteAvailableRollback(data);
                        deleteRollback(data);
                        break;
                        break;
                    }
                    }
                }
                }
@@ -464,13 +465,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
            final List<RollbackData> changed;
            final List<RollbackData> changed;
            synchronized (mLock) {
            synchronized (mLock) {
                ensureRollbackDataLoadedLocked();
                ensureRollbackDataLoadedLocked();
                changed = mUserdataHelper.computePendingBackupsAndRestores(userId,
                changed = mAppDataRollbackHelper.computePendingBackupsAndRestores(userId,
                        pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks,
                        pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks,
                        mRecentlyExecutedRollbacks);
                        mRecentlyExecutedRollbacks);
            }
            }


            mUserdataHelper.commitPendingBackupAndRestoreForUser(userId,
            mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId,
                    pendingBackupPackages, pendingRestorePackages);
                    pendingBackupPackages, pendingRestorePackages, changed);


            for (RollbackData rd : changed) {
            for (RollbackData rd : changed) {
                try {
                try {
@@ -520,7 +521,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                        // mAvailableRollbacks, or is it okay to leave as
                        // mAvailableRollbacks, or is it okay to leave as
                        // unavailable until the next reboot when it will go
                        // unavailable until the next reboot when it will go
                        // away on its own?
                        // away on its own?
                        mRollbackStore.deleteAvailableRollback(data);
                        deleteRollback(data);
                    }
                    }
                }
                }
            }
            }
@@ -592,7 +593,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                                        info.getVersionRolledBackFrom(),
                                        info.getVersionRolledBackFrom(),
                                        installedVersion)) {
                                        installedVersion)) {
                        iter.remove();
                        iter.remove();
                        mRollbackStore.deleteAvailableRollback(data);
                        deleteRollback(data);
                        break;
                        break;
                    }
                    }
                }
                }
@@ -705,7 +706,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {


                if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
                if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
                    iter.remove();
                    iter.remove();
                    mRollbackStore.deleteAvailableRollback(data);
                    deleteRollback(data);
                } else if (oldest == null || oldest.isAfter(data.timestamp)) {
                } else if (oldest == null || oldest.isAfter(data.timestamp)) {
                    oldest = data.timestamp;
                    oldest = data.timestamp;
                }
                }
@@ -821,9 +822,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
            String packageName = newPackage.packageName;
            String packageName = newPackage.packageName;
            for (PackageRollbackInfo info : rd.packages) {
            for (PackageRollbackInfo info : rd.packages) {
                if (info.getPackageName().equals(packageName)) {
                if (info.getPackageName().equals(packageName)) {
                    IntArray pendingBackups = mUserdataHelper.snapshotAppData(
                    AppDataRollbackHelper.SnapshotAppDataResult rs =
                            packageName, installedUsers);
                            mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers);
                    info.getPendingBackups().addAll(pendingBackups);
                    info.getPendingBackups().addAll(rs.pendingBackups);
                    for (int i = 0; i < rs.ceSnapshotInodes.size(); i++) {
                        info.putCeSnapshotInode(rs.ceSnapshotInodes.keyAt(i),
                                rs.ceSnapshotInodes.valueAt(i));
                    }
                    try {
                    try {
                        mRollbackStore.saveAvailableRollback(rd);
                        mRollbackStore.saveAvailableRollback(rd);
                    } catch (IOException ioe) {
                    } catch (IOException ioe) {
@@ -892,13 +897,18 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
        VersionedPackage installedVersion = new VersionedPackage(packageName,
        VersionedPackage installedVersion = new VersionedPackage(packageName,
                pkgInfo.getLongVersionCode());
                pkgInfo.getLongVersionCode());


        IntArray pendingBackups = IntArray.wrap(new int[0]);
        final AppDataRollbackHelper.SnapshotAppDataResult result;
        if (snapshotUserData && !isApex) {
        if (snapshotUserData && !isApex) {
            pendingBackups = mUserdataHelper.snapshotAppData(packageName, installedUsers);
            result = mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers);
        } else {
            result = new AppDataRollbackHelper.SnapshotAppDataResult(IntArray.wrap(new int[0]),
                new SparseLongArray());
        }
        }


        PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion,
        PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion,
                pendingBackups, new ArrayList<>(), isApex);
                result.pendingBackups, new ArrayList<>(), isApex, IntArray.wrap(installedUsers),
                result.ceSnapshotInodes);

        RollbackData data;
        RollbackData data;
        try {
        try {
            int childSessionId = session.getSessionId();
            int childSessionId = session.getSessionId();
@@ -948,9 +958,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
        getHandler().post(() -> {
        getHandler().post(() -> {
            final RollbackData rollbackData = getRollbackForPackage(packageName);
            final RollbackData rollbackData = getRollbackForPackage(packageName);
            for (int userId : userIds) {
            for (int userId : userIds) {
                final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName,
                final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData(
                        rollbackData, userId, appId, ceDataInode, seInfo);
                        packageName, rollbackData, userId, appId, ceDataInode, seInfo);

                // We've updated metadata about this rollback, so save it to flash.
                // We've updated metadata about this rollback, so save it to flash.
                if (changedRollbackData) {
                if (changedRollbackData) {
                    try {
                    try {
@@ -1142,12 +1151,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
                    scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
                    scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
                } catch (IOException e) {
                } catch (IOException e) {
                    Log.e(TAG, "Unable to enable rollback", e);
                    Log.e(TAG, "Unable to enable rollback", e);
                    mRollbackStore.deleteAvailableRollback(data);
                    deleteRollback(data);
                }
                }
            } else {
            } else {
                // The install session was aborted, clean up the pending
                // The install session was aborted, clean up the pending
                // install.
                // install.
                mRollbackStore.deleteAvailableRollback(data);
                deleteRollback(data);
            }
            }
        }
        }
    }
    }
@@ -1246,4 +1255,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {


        throw new IOException("Failed to allocate rollback ID");
        throw new IOException("Failed to allocate rollback ID");
    }
    }

    private void deleteRollback(RollbackData rollbackData) {
        for (PackageRollbackInfo info : rollbackData.packages) {
            IntArray installedUsers = info.getInstalledUsers();
            SparseLongArray ceSnapshotInodes = info.getCeSnapshotInodes();
            for (int i = 0; i < installedUsers.size(); i++) {
                int userId = installedUsers.get(i);
                mAppDataRollbackHelper.destroyAppDataSnapshot(info.getPackageName(), userId,
                        ceSnapshotInodes.get(userId, 0));
            }
        }
        mRollbackStore.deleteAvailableRollback(rollbackData);
    }
}
}
+32 −3
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ import android.content.rollback.PackageRollbackInfo.RestoreInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackInfo;
import android.util.IntArray;
import android.util.IntArray;
import android.util.Log;
import android.util.Log;
import android.util.SparseLongArray;


import libcore.io.IoUtils;
import libcore.io.IoUtils;


@@ -160,6 +161,28 @@ class RollbackStore {
        return restoreInfos;
        return restoreInfos;
    }
    }


    private static @NonNull JSONArray ceSnapshotInodesToJson(
            @NonNull SparseLongArray ceSnapshotInodes) throws JSONException {
        JSONArray array = new JSONArray();
        for (int i = 0; i < ceSnapshotInodes.size(); i++) {
            JSONObject entryJson = new JSONObject();
            entryJson.put("userId", ceSnapshotInodes.keyAt(i));
            entryJson.put("ceSnapshotInode", ceSnapshotInodes.valueAt(i));
            array.put(entryJson);
        }
        return array;
    }

    private static @NonNull SparseLongArray ceSnapshotInodesFromJson(JSONArray json)
            throws JSONException {
        SparseLongArray ceSnapshotInodes = new SparseLongArray(json.length());
        for (int i = 0; i < json.length(); i++) {
            JSONObject entry = json.getJSONObject(i);
            ceSnapshotInodes.append(entry.getInt("userId"), entry.getLong("ceSnapshotInode"));
        }
        return ceSnapshotInodes;
    }

    /**
    /**
     * Reads the list of recently executed rollbacks from persistent storage.
     * Reads the list of recently executed rollbacks from persistent storage.
     */
     */
@@ -263,8 +286,6 @@ class RollbackStore {
     * rollback.
     * rollback.
     */
     */
    void deleteAvailableRollback(RollbackData data) {
    void deleteAvailableRollback(RollbackData data) {
        // TODO(narayan): Make sure we delete the userdata snapshot along with the backup of the
        // actual app.
        removeFile(data.backupDir);
        removeFile(data.backupDir);
    }
    }


@@ -341,11 +362,15 @@ class RollbackStore {


        IntArray pendingBackups = info.getPendingBackups();
        IntArray pendingBackups = info.getPendingBackups();
        List<RestoreInfo> pendingRestores = info.getPendingRestores();
        List<RestoreInfo> pendingRestores = info.getPendingRestores();
        IntArray installedUsers = info.getInstalledUsers();
        json.put("pendingBackups", convertToJsonArray(pendingBackups));
        json.put("pendingBackups", convertToJsonArray(pendingBackups));
        json.put("pendingRestores", convertToJsonArray(pendingRestores));
        json.put("pendingRestores", convertToJsonArray(pendingRestores));


        json.put("isApex", info.isApex());
        json.put("isApex", info.isApex());


        json.put("installedUsers", convertToJsonArray(installedUsers));
        json.put("ceSnapshotInodes", ceSnapshotInodesToJson(info.getCeSnapshotInodes()));

        return json;
        return json;
    }
    }


@@ -362,8 +387,12 @@ class RollbackStore {


        final boolean isApex = json.getBoolean("isApex");
        final boolean isApex = json.getBoolean("isApex");


        final IntArray installedUsers = convertToIntArray(json.getJSONArray("installedUsers"));
        final SparseLongArray ceSnapshotInodes = ceSnapshotInodesFromJson(
                json.getJSONArray("ceSnapshotInodes"));

        return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo,
        return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo,
                pendingBackups, pendingRestores, isApex);
                pendingBackups, pendingRestores, isApex, installedUsers, ceSnapshotInodes);
    }
    }


    private JSONArray versionedPackagesToJson(List<VersionedPackage> packages)
    private JSONArray versionedPackagesToJson(List<VersionedPackage> packages)
Loading