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

Commit 942bf4dd authored by Oli Lan's avatar Oli Lan Committed by Android (Google) Code Review
Browse files

Merge "Record supported extension versions at rollback creation."

parents 0b2370c2 8c7c8faa
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -38,6 +38,9 @@ public class SdkExtensions {

    private static final int R_EXTENSION_INT;
    static {
        // Note: when adding more extension versions, the logic that records current
        // extension versions when saving a rollback must also be updated.
        // At the time of writing this is in RollbackManagerServiceImpl#getExtensionVersions()
        R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
    }

+22 −3
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.SparseLongArray;

import com.android.internal.annotations.GuardedBy;
@@ -172,6 +173,12 @@ class Rollback {
    @GuardedBy("mLock")
    private int mNumPackageSessionsWithSuccess;

    /**
     * The extension versions supported at the time of rollback creation. May be null if not set
     * at creation time.
     */
    @Nullable private final SparseIntArray mExtensionVersions;

    /**
     * Constructs a new, empty Rollback instance.
     *
@@ -181,9 +188,11 @@ class Rollback {
     * @param userId the user that performed the install with rollback enabled.
     * @param installerPackageName the installer package name from the original install session.
     * @param packageSessionIds the session ids for all packages in the install.
     * @param extensionVersions the extension versions supported at the time of rollback creation
     */
    Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
            String installerPackageName, int[] packageSessionIds) {
            String installerPackageName, int[] packageSessionIds,
            SparseIntArray extensionVersions) {
        this.info = new RollbackInfo(rollbackId,
                /* packages */ new ArrayList<>(),
                /* isStaged */ stagedSessionId != -1,
@@ -196,11 +205,12 @@ class Rollback {
        mState = ROLLBACK_STATE_ENABLING;
        mTimestamp = Instant.now();
        mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
        mExtensionVersions = extensionVersions;
    }

    Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
             String installerPackageName) {
        this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null);
        this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null, null);
    }

    /**
@@ -208,7 +218,7 @@ class Rollback {
     */
    Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
            @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress,
            int userId, String installerPackageName) {
            int userId, String installerPackageName, SparseIntArray extensionVersions) {
        this.info = info;
        mUserId = userId;
        mInstallerPackageName = installerPackageName;
@@ -218,6 +228,7 @@ class Rollback {
        mState = state;
        mApkSessionId = apkSessionId;
        mRestoreUserDataInProgress = restoreUserDataInProgress;
        mExtensionVersions = extensionVersions;
        // TODO(b/120200473): Include this field during persistence. This field will be used to
        // decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting
        // this field is not backward compatible. We won't fix b/120200473 until S to minimize the
@@ -281,6 +292,14 @@ class Rollback {
        return mInstallerPackageName;
    }

    /**
     * Returns the extension versions that were supported at the time that the rollback was created,
     * as a mapping from SdkVersion to ExtensionVersion.
     */
    @Nullable SparseIntArray getExtensionVersions() {
        return mExtensionVersions;
    }

    /**
     * Returns true if the rollback is in the ENABLING state.
     */
+18 −2
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.content.rollback.IRollbackManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -49,12 +50,14 @@ import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.ext.SdkExtensions;
import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Log;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
@@ -1312,16 +1315,29 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
        final Rollback rollback;
        if (parentSession.isStaged()) {
            rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
                    installerPackageName, packageSessionIds);
                    installerPackageName, packageSessionIds, getExtensionVersions());
        } else {
            rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
                    installerPackageName, packageSessionIds);
                    installerPackageName, packageSessionIds, getExtensionVersions());
        }

        mRollbacks.add(rollback);
        return rollback;
    }

    private SparseIntArray getExtensionVersions() {
        // This list must be updated whenever the current API level is increased, or should be
        // replaced when we have another way of determining the relevant SDK versions.
        final int[] relevantSdkVersions = { Build.VERSION_CODES.R };

        SparseIntArray result = new SparseIntArray(relevantSdkVersions.length);
        for (int i = 0; i < relevantSdkVersions.length; i++) {
            result.put(relevantSdkVersions[i],
                    SdkExtensions.getExtensionVersion(relevantSdkVersions[i]));
        }
        return result;
    }

    /**
     * Returns the Rollback associated with the given session if parent or child session id matches.
     * Returns null if not found.
+40 −5
Original line number Diff line number Diff line
@@ -21,12 +21,14 @@ import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.rollback.Rollback.rollbackStateFromString;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.PackageRollbackInfo.RestoreInfo;
import android.content.rollback.RollbackInfo;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.SparseLongArray;

import com.android.internal.annotations.GuardedBy;
@@ -171,6 +173,35 @@ class RollbackStore {
        return ceSnapshotInodes;
    }

    private static @Nullable JSONArray extensionVersionsToJson(
            @Nullable SparseIntArray extensionVersions) throws JSONException {
        if (extensionVersions == null) {
            return null;
        }
        JSONArray array = new JSONArray();
        for (int i = 0; i < extensionVersions.size(); i++) {
            JSONObject entryJson = new JSONObject();
            entryJson.put("sdkVersion", extensionVersions.keyAt(i));
            entryJson.put("extensionVersion", extensionVersions.valueAt(i));
            array.put(entryJson);
        }
        return array;
    }

    private static @Nullable SparseIntArray extensionVersionsFromJson(@Nullable JSONArray json)
            throws JSONException {
        if (json == null) {
            return null;
        }
        SparseIntArray extensionVersions = new SparseIntArray(json.length());
        for (int i = 0; i < json.length(); i++) {
            JSONObject entry = json.getJSONObject(i);
            extensionVersions.append(
                    entry.getInt("sdkVersion"), entry.getInt("extensionVersion"));
        }
        return extensionVersions;
    }

    private static JSONObject rollbackInfoToJson(RollbackInfo rollback) throws JSONException {
        JSONObject json = new JSONObject();
        json.put("rollbackId", rollback.getRollbackId());
@@ -195,10 +226,10 @@ class RollbackStore {
     * backupDir assigned.
     */
    Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
            int[] packageSessionIds) {
            int[] packageSessionIds, SparseIntArray extensionVersions) {
        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
        return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
                packageSessionIds);
                packageSessionIds, extensionVersions);
    }

    /**
@@ -206,10 +237,11 @@ class RollbackStore {
     * backupDir assigned.
     */
    Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
            String installerPackageName, int[] packageSessionIds) {
            String installerPackageName, int[] packageSessionIds,
            SparseIntArray extensionVersions) {
        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
        return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
                packageSessionIds);
                packageSessionIds, extensionVersions);
    }

    /**
@@ -267,6 +299,8 @@ class RollbackStore {
            dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
            dataJson.put("userId", rollback.getUserId());
            dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName());
            dataJson.putOpt(
                    "extensionVersions", extensionVersionsToJson(rollback.getExtensionVersions()));

            PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
            pw.println(dataJson.toString());
@@ -311,7 +345,8 @@ class RollbackStore {
                dataJson.getInt("apkSessionId"),
                dataJson.getBoolean("restoreUserDataInProgress"),
                dataJson.optInt("userId", USER_SYSTEM),
                dataJson.optString("installerPackageName", ""));
                dataJson.optString("installerPackageName", ""),
                extensionVersionsFromJson(dataJson.optJSONArray("extensionVersions")));
    }

    private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
+104 −6
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;

import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.util.SparseIntArray;
import android.util.SparseLongArray;

import com.google.common.truth.Correspondence;
@@ -82,7 +83,7 @@ public class RollbackStoreTest {
                }
            };

    private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
    private static final String JSON_ROLLBACK_NO_EXT = "{'info':{'rollbackId':123,'packages':"
            + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
            + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
            + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
@@ -102,6 +103,28 @@ public class RollbackStoreTest {
            + "'restoreUserDataInProgress':true, 'userId':0,"
            + "'installerPackageName':'some.installer'}";

    private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
            + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
            + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
            + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
            + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'isApkInApex':false,"
            + "'installedUsers':"
            + "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6},"
            + "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546,"
            + "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips',"
            + "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test',"
            + "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18,"
            + "'appId':-12,'seInfo':''}],'isApex':false,'isApkInApex':false,"
            + "'installedUsers':[55,79],"
            + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
            + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
            + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
            + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
            + "'restoreUserDataInProgress':true, 'userId':0,"
            + "'installerPackageName':'some.installer',"
            + "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25},"
            + "{'sdkVersion':30,'extensionVersion':71}]}";

    @Rule
    public TemporaryFolder mFolder = new TemporaryFolder();

@@ -118,7 +141,10 @@ public class RollbackStoreTest {

    @Test
    public void createNonStaged() {
        Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
        SparseIntArray extensionVersions = new SparseIntArray();
        extensionVersions.put(30, 71);
        Rollback rollback = mRollbackStore.createNonStagedRollback(
                ID, USER, INSTALLER, null, extensionVersions);

        assertThat(rollback.getBackupDir().getAbsolutePath())
                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -127,11 +153,16 @@ public class RollbackStoreTest {
        assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
        assertThat(rollback.info.getPackages()).isEmpty();
        assertThat(rollback.isEnabling()).isTrue();
        assertThat(rollback.getExtensionVersions().toString())
                .isEqualTo(extensionVersions.toString());
    }

    @Test
    public void createStaged() {
        Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER, null);
        SparseIntArray extensionVersions = new SparseIntArray();
        extensionVersions.put(30, 71);
        Rollback rollback = mRollbackStore.createStagedRollback(
                ID, 897, USER, INSTALLER, null, extensionVersions);

        assertThat(rollback.getBackupDir().getAbsolutePath())
                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -142,11 +173,17 @@ public class RollbackStoreTest {
        assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
        assertThat(rollback.info.getPackages()).isEmpty();
        assertThat(rollback.isEnabling()).isTrue();
        assertThat(rollback.getExtensionVersions().toString())
                .isEqualTo(extensionVersions.toString());
    }

    @Test
    public void saveAndLoadRollback() {
        Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
        SparseIntArray extensionVersions = new SparseIntArray();
        extensionVersions.put(5, 25);
        extensionVersions.put(30, 71);
        Rollback origRb = mRollbackStore.createNonStagedRollback(
                ID, USER, INSTALLER, null, extensionVersions);

        origRb.setRestoreUserDataInProgress(true);
        origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -194,9 +231,63 @@ public class RollbackStoreTest {
        assertRollbacksAreEquivalent(loadedRb, origRb);
    }

    @Test
    public void loadFromJsonNoExtensionVersions() throws Exception {
        Rollback expectedRb =
                mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null);

        expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
        expectedRb.setRestoreUserDataInProgress(true);
        expectedRb.info.getCausePackages().add(new VersionedPackage("hello", 23));
        expectedRb.info.getCausePackages().add(new VersionedPackage("something", 999));
        expectedRb.info.setCommittedSessionId(45654465);

        PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55),
                new VersionedPackage("blah1", 50), new ArrayList<>(), new ArrayList<>(),
                false, false, new ArrayList<>(), new SparseLongArray());
        pkgInfo1.getPendingBackups().add(59);
        pkgInfo1.getPendingBackups().add(1245);
        pkgInfo1.getPendingBackups().add(124544);
        pkgInfo1.getCeSnapshotInodes().put(546546, 345689375);
        pkgInfo1.getCeSnapshotInodes().put(2222, 81641654445L);
        pkgInfo1.getCeSnapshotInodes().put(1, -6);

        pkgInfo1.getPendingRestores().add(
                new PackageRollbackInfo.RestoreInfo(498, 32322, "wombles"));
        pkgInfo1.getPendingRestores().add(
                new PackageRollbackInfo.RestoreInfo(-895, 1, "pingu"));

        pkgInfo1.getSnapshottedUsers().add(498468432);
        pkgInfo1.getSnapshottedUsers().add(1111);
        pkgInfo1.getSnapshottedUsers().add(98464);

        PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28),
                new VersionedPackage("com.chips.test", 48), new ArrayList<>(), new ArrayList<>(),
                false, false, new ArrayList<>(), new SparseLongArray());
        pkgInfo2.getPendingBackups().add(5);

        pkgInfo2.getPendingRestores().add(
                new PackageRollbackInfo.RestoreInfo(18, -12, ""));

        pkgInfo2.getSnapshottedUsers().add(55);
        pkgInfo2.getSnapshottedUsers().add(79);

        expectedRb.info.getPackages().add(pkgInfo1);
        expectedRb.info.getPackages().add(pkgInfo2);

        Rollback parsedRb = RollbackStore.rollbackFromJson(
                new JSONObject(JSON_ROLLBACK_NO_EXT), expectedRb.getBackupDir());

        assertRollbacksAreEquivalent(parsedRb, expectedRb);
    }

    @Test
    public void loadFromJson() throws Exception {
        Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
        SparseIntArray extensionVersions = new SparseIntArray();
        extensionVersions.put(5, 25);
        extensionVersions.put(30, 71);
        Rollback expectedRb = mRollbackStore.createNonStagedRollback(
                ID, USER, INSTALLER, null, extensionVersions);

        expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
        expectedRb.setRestoreUserDataInProgress(true);
@@ -245,7 +336,7 @@ public class RollbackStoreTest {

    @Test
    public void saveAndDelete() {
        Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
        Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null, null);

        RollbackStore.saveRollback(rollback);

@@ -293,6 +384,13 @@ public class RollbackStoreTest {

        assertThat(a.getUserId()).isEqualTo(b.getUserId());
        assertThat(a.getInstallerPackageName()).isEqualTo(b.getInstallerPackageName());

        if (a.getExtensionVersions() == null) {
            assertThat(b.getExtensionVersions()).isNull();
        } else {
            assertThat(b.getExtensionVersions().toString())
                    .isEqualTo(a.getExtensionVersions().toString());
        }
    }

    private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
Loading