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

Commit 0987cb03 authored by Gavin Corkery's avatar Gavin Corkery
Browse files

Use userId and package installer name when committing rollbacks

Currently when committing a rollback, the system user is used
for the package context for the downgrade session, even when
the package was not installed as this user. This leads to a
package being installed to the system user in the cases where
it is only installed to a secondary user.

The installer package
name is also currently retrieved from package manager (and does
not work for apex), so storing this information when enabling
the rollback will be useful.

This change should be compatible with previous rollbacks stored
on the device.

Bug: 133210624
Test: atest RollbackTest
Test: atest SecondaryUserRollbackTest
Test: Install test APK v1 and v2 with rollback enabled for a secondary
user, rollback and verify the package is not installed for
the system user

Change-Id: I16346120cf9ba7b04254ab5e8fa5090be7d562ac
parent 2f495227
Loading
Loading
Loading
Loading
+51 −12
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure
import android.Manifest;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender;
@@ -33,7 +34,9 @@ import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.content.rollback.RollbackManager;
import android.os.Binder;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.IntArray;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseLongArray;
import android.util.SparseLongArray;
@@ -141,19 +144,38 @@ class Rollback {
     */
     */
    private final Object mLock = new Object();
    private final Object mLock = new Object();


    /**
     * The user that performed the install with rollback enabled.
     */
    public final int mUserId;

    /**
     * The installer package name from the install session that enabled the rollback. May be null if
     * that session did not set this value.
     *
     * If this is an empty string then the installer package name will be resolved by
     * PackageManager.
     */
    @Nullable public final String mInstallerPackageName;

    /**
    /**
     * Constructs a new, empty Rollback instance.
     * Constructs a new, empty Rollback instance.
     *
     *
     * @param rollbackId the id of the rollback.
     * @param rollbackId the id of the rollback.
     * @param backupDir the directory where the rollback data is stored.
     * @param backupDir the directory where the rollback data is stored.
     * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
     * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
     * @param userId the user that performed the install with rollback enabled.
     * @param installerPackageName the installer package name from the original install session.
     */
     */
    Rollback(int rollbackId, File backupDir, int stagedSessionId) {
    Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
            String installerPackageName) {
        this.info = new RollbackInfo(rollbackId,
        this.info = new RollbackInfo(rollbackId,
                /* packages */ new ArrayList<>(),
                /* packages */ new ArrayList<>(),
                /* isStaged */ stagedSessionId != -1,
                /* isStaged */ stagedSessionId != -1,
                /* causePackages */ new ArrayList<>(),
                /* causePackages */ new ArrayList<>(),
                /* committedSessionId */ -1);
                /* committedSessionId */ -1);
        mUserId = userId;
        mInstallerPackageName = installerPackageName;
        mBackupDir = backupDir;
        mBackupDir = backupDir;
        mStagedSessionId = stagedSessionId;
        mStagedSessionId = stagedSessionId;
        mState = ROLLBACK_STATE_ENABLING;
        mState = ROLLBACK_STATE_ENABLING;
@@ -164,8 +186,11 @@ class Rollback {
     * Constructs a pre-populated Rollback instance.
     * Constructs a pre-populated Rollback instance.
     */
     */
    Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
    Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
            @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress) {
            @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress,
            int userId, String installerPackageName) {
        this.info = info;
        this.info = info;
        mUserId = userId;
        mInstallerPackageName = installerPackageName;
        mBackupDir = backupDir;
        mBackupDir = backupDir;
        mTimestamp = timestamp;
        mTimestamp = timestamp;
        mStagedSessionId = stagedSessionId;
        mStagedSessionId = stagedSessionId;
@@ -215,6 +240,21 @@ class Rollback {
        return mStagedSessionId;
        return mStagedSessionId;
    }
    }


    /**
     * Returns the ID of the user that performed the install with rollback enabled.
     */
    int getUserId() {
        return mUserId;
    }

    /**
     * Returns the installer package name from the install session that enabled the rollback. In the
     * case that this is called on a rollback from an older version, returns the empty string.
     */
    @Nullable String getInstallerPackageName() {
        return mInstallerPackageName;
    }

    /**
    /**
     * Returns true if the rollback is in the ENABLING state.
     * Returns true if the rollback is in the ENABLING state.
     */
     */
@@ -360,7 +400,8 @@ class Rollback {
            // Get a context to use to install the downgraded version of the package.
            // Get a context to use to install the downgraded version of the package.
            Context pkgContext;
            Context pkgContext;
            try {
            try {
                pkgContext = context.createPackageContext(callerPackageName, 0);
                pkgContext = context.createPackageContextAsUser(callerPackageName, 0,
                        UserHandle.of(mUserId));
            } catch (PackageManager.NameNotFoundException e) {
            } catch (PackageManager.NameNotFoundException e) {
                sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
                sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
                        "Invalid callerPackageName");
                        "Invalid callerPackageName");
@@ -385,16 +426,14 @@ class Rollback {
                for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
                for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
                    PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                    PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                            PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                            PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                    // TODO: We can't get the installerPackageName for apex
                    String installerPackageName = mInstallerPackageName;
                    // (b/123920130). Is it okay to ignore the installer package
                    if (TextUtils.isEmpty(mInstallerPackageName)) {
                    // for apex?
                        installerPackageName = pm.getInstallerPackageName(
                    if (!pkgRollbackInfo.isApex()) {
                                pkgRollbackInfo.getPackageName());
                        String installerPackageName =
                    }
                                pm.getInstallerPackageName(pkgRollbackInfo.getPackageName());
                    if (installerPackageName != null) {
                    if (installerPackageName != null) {
                        params.setInstallerPackageName(installerPackageName);
                        params.setInstallerPackageName(installerPackageName);
                    }
                    }
                    }
                    params.setRequestDowngrade(true);
                    params.setRequestDowngrade(true);
                    params.setRequiredInstalledVersionCode(
                    params.setRequiredInstalledVersionCode(
                            pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
                            pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
+11 −2
Original line number Original line Diff line number Diff line
@@ -1248,13 +1248,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
    private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
        int rollbackId = allocateRollbackIdLocked();
        int rollbackId = allocateRollbackIdLocked();
        final int userId;
        if (parentSession.getUser() == UserHandle.ALL) {
            userId = UserHandle.USER_SYSTEM;
        } else {
            userId = parentSession.getUser().getIdentifier();
        }
        String installerPackageName = parentSession.getInstallerPackageName();
        final Rollback rollback;
        final Rollback rollback;
        int parentSessionId = parentSession.getSessionId();
        int parentSessionId = parentSession.getSessionId();


        if (parentSession.isStaged()) {
        if (parentSession.isStaged()) {
            rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
            rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
                    installerPackageName);
        } else {
        } else {
            rollback = mRollbackStore.createNonStagedRollback(rollbackId);
            rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
                    installerPackageName);
        }
        }


        int[] packageSessionIds;
        int[] packageSessionIds;
+12 −5
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.server.rollback;
package com.android.server.rollback;


import static android.os.UserHandle.USER_SYSTEM;

import static com.android.server.rollback.Rollback.rollbackStateFromString;
import static com.android.server.rollback.Rollback.rollbackStateFromString;


import android.annotation.NonNull;
import android.annotation.NonNull;
@@ -196,18 +198,19 @@ class RollbackStore {
     * Creates a new Rollback instance for a non-staged rollback with
     * Creates a new Rollback instance for a non-staged rollback with
     * backupDir assigned.
     * backupDir assigned.
     */
     */
    Rollback createNonStagedRollback(int rollbackId) {
    Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName) {
        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
        return new Rollback(rollbackId, backupDir, -1);
        return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName);
    }
    }


    /**
    /**
     * Creates a new Rollback instance for a staged rollback with
     * Creates a new Rollback instance for a staged rollback with
     * backupDir assigned.
     * backupDir assigned.
     */
     */
    Rollback createStagedRollback(int rollbackId, int stagedSessionId) {
    Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
            String installerPackageName) {
        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
        return new Rollback(rollbackId, backupDir, stagedSessionId);
        return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName);
    }
    }


    /**
    /**
@@ -263,6 +266,8 @@ class RollbackStore {
            dataJson.put("state", rollback.getStateAsString());
            dataJson.put("state", rollback.getStateAsString());
            dataJson.put("apkSessionId", rollback.getApkSessionId());
            dataJson.put("apkSessionId", rollback.getApkSessionId());
            dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
            dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
            dataJson.put("userId", rollback.getUserId());
            dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName());


            PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
            PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
            pw.println(dataJson.toString());
            pw.println(dataJson.toString());
@@ -305,7 +310,9 @@ class RollbackStore {
                dataJson.getInt("stagedSessionId"),
                dataJson.getInt("stagedSessionId"),
                rollbackStateFromString(dataJson.getString("state")),
                rollbackStateFromString(dataJson.getString("state")),
                dataJson.getInt("apkSessionId"),
                dataJson.getInt("apkSessionId"),
                dataJson.getBoolean("restoreUserDataInProgress"));
                dataJson.getBoolean("restoreUserDataInProgress"),
                dataJson.optInt("userId", USER_SYSTEM),
                dataJson.optString("installerPackageName", ""));
    }
    }


    private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
    private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
+9 −6
Original line number Original line Diff line number Diff line
@@ -106,6 +106,11 @@ public class AppDataRollbackHelperTest {
        return createPackageRollbackInfo(packageName, new int[] {});
        return createPackageRollbackInfo(packageName, new int[] {});
    }
    }


    private static Rollback createRollbackForId(int rollbackId) {
        return new Rollback(rollbackId, new File("/does/not/exist"), -1,
                0, "com.xyz");
    }

    @Test
    @Test
    public void testRestoreAppDataSnapshot_pendingBackupForUser() throws Exception {
    public void testRestoreAppDataSnapshot_pendingBackupForUser() throws Exception {
        Installer installer = mock(Installer.class);
        Installer installer = mock(Installer.class);
@@ -232,18 +237,16 @@ public class AppDataRollbackHelperTest {
        wasRecentlyRestored.getPendingRestores().add(
        wasRecentlyRestored.getPendingRestores().add(
                new RestoreInfo(73 /* userId */, 239 /* appId*/, "seInfo"));
                new RestoreInfo(73 /* userId */, 239 /* appId*/, "seInfo"));


        Rollback dataWithPendingBackup = new Rollback(101, new File("/does/not/exist"), -1);
        Rollback dataWithPendingBackup = createRollbackForId(101);
        dataWithPendingBackup.info.getPackages().add(pendingBackup);
        dataWithPendingBackup.info.getPackages().add(pendingBackup);


        Rollback dataWithRecentRestore = new Rollback(17239, new File("/does/not/exist"),
        Rollback dataWithRecentRestore = createRollbackForId(17239);
                -1);
        dataWithRecentRestore.info.getPackages().add(wasRecentlyRestored);
        dataWithRecentRestore.info.getPackages().add(wasRecentlyRestored);


        Rollback dataForDifferentUser = new Rollback(17239, new File("/does/not/exist"),
        Rollback dataForDifferentUser = createRollbackForId(17239);
                -1);
        dataForDifferentUser.info.getPackages().add(ignoredInfo);
        dataForDifferentUser.info.getPackages().add(ignoredInfo);


        Rollback dataForRestore = new Rollback(17239, new File("/does/not/exist"), -1);
        Rollback dataForRestore = createRollbackForId(17239);
        dataForRestore.info.getPackages().add(pendingRestore);
        dataForRestore.info.getPackages().add(pendingRestore);
        dataForRestore.info.getPackages().add(wasRecentlyRestored);
        dataForRestore.info.getPackages().add(wasRecentlyRestored);


+12 −6
Original line number Original line Diff line number Diff line
@@ -43,6 +43,8 @@ import java.util.Objects;
public class RollbackStoreTest {
public class RollbackStoreTest {


    private static final int ID = 123;
    private static final int ID = 123;
    private static final int USER = 0;
    private static final String INSTALLER = "some.installer";


    private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR =
    private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR =
            new Correspondence<VersionedPackage, VersionedPackage>() {
            new Correspondence<VersionedPackage, VersionedPackage>() {
@@ -97,7 +99,8 @@ public class RollbackStoreTest {
            + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
            + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
            + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
            + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
            + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
            + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
            + "'restoreUserDataInProgress':true}";
            + "'restoreUserDataInProgress':true, 'userId':0,"
            + "'installerPackageName':'some.installer'}";


    @Rule
    @Rule
    public TemporaryFolder mFolder = new TemporaryFolder();
    public TemporaryFolder mFolder = new TemporaryFolder();
@@ -115,7 +118,7 @@ public class RollbackStoreTest {


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


        assertThat(rollback.getBackupDir().getAbsolutePath())
        assertThat(rollback.getBackupDir().getAbsolutePath())
                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -128,7 +131,7 @@ public class RollbackStoreTest {


    @Test
    @Test
    public void createStaged() {
    public void createStaged() {
        Rollback rollback = mRollbackStore.createStagedRollback(ID, 897);
        Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER);


        assertThat(rollback.getBackupDir().getAbsolutePath())
        assertThat(rollback.getBackupDir().getAbsolutePath())
                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -143,7 +146,7 @@ public class RollbackStoreTest {


    @Test
    @Test
    public void saveAndLoadRollback() {
    public void saveAndLoadRollback() {
        Rollback origRb = mRollbackStore.createNonStagedRollback(ID);
        Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);


        origRb.setRestoreUserDataInProgress(true);
        origRb.setRestoreUserDataInProgress(true);
        origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
        origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -193,7 +196,7 @@ public class RollbackStoreTest {


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


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


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


        RollbackStore.saveRollback(rollback);
        RollbackStore.saveRollback(rollback);


@@ -287,6 +290,9 @@ public class RollbackStoreTest {
            assertPackageRollbacksAreEquivalent(
            assertPackageRollbacksAreEquivalent(
                    b.info.getPackages().get(i), a.info.getPackages().get(i));
                    b.info.getPackages().get(i), a.info.getPackages().get(i));
        }
        }

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


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